对于
instance Alternative []
,
(<|>) = (++)
. 所以我认为
(<|>)
作为某种类型的拼接器,导致看起来几乎通用的容器转换器:
-- (<|>) = generalization of (++)
(<|) :: Alternative f => x -> f x -> f x
x <| xs = pure x <|> xs
conv :: (Foldable t, Alternative f) => t x -> f x
conv = foldr (<|) empty
事实上,我能够概括
全部的
功能来自
Data.List
,以下是一些:
-- fmap = generalization of map
reverse :: (Foldable t, Alternative f) => t a -> f a
reverse = getAlt . getDual . foldMap (Dual . Alt . pure)
-- asum = generalization of concat
asumMap :: (Foldable t, Alternative f) => (a -> f b) -> t a -> f b -- generalization of concatMap
asumMap f = getAlt . foldMap (Alt . f)
splitAt :: (Foldable t, Alternative f, Alternative g) => Int -> t a -> (f a, g a)
splitAt n xs = let (_,fs,gs) = foldl' (\(i,z1,z2) x -> if 0 < i then (i-1,z1 . (x <|),z2) else (0,z1,z2 . (x <|))) (n,id,id) xs in (fs empty,gs empty)
此外,这种类比还产生了一些有趣的新实例,例如用于函子和的工作应用函子(
Data.Functor.Sum
):
instance (Foldable f, Applicative f, Alternative g) => Applicative (Sum f g) where
pure = InL . pure
InL f <*> InL x = InL (f <*> x)
InL f <*> InR x = InR (conv f <*> x)
InR f <*> InL x = InR (f <*> conv x)
InR f <*> InR x = InR (f <*> x)
instance (Foldable f, Applicative f, Alternative g) => Alternative (Sum f g) where
empty = InR empty
InL x <|> _ = InL x
InR _ <|> InL y = InL y
InR x <|> InR y = InR (x <|> y)
概括一下真的是个好主意吗
全部的
函数并使用这种类比创建新实例,尤其是列表操作?
编辑:我特别关注不明确的返回类型。对于普通的列表操作,返回类型可以从其参数类型中推断出来。但“通用”版本不是,因为必须显式指定返回类型。这个问题是否严重到足以将这个类比视为危险的程度?(或者是否有其他问题?)
编辑2:如果我了解
foldl'
没错,宇宙的时间复杂性
splitAt
(如上所示)必须
Î(length xs)
像
福尔德
对每个元素都很严格,对吗?如果是,那一定是个问题,因为它不如常规版本
Î(min n (length xs))
.