For instance Alternative []
, (<|>) = (++)
. So I regarded (<|>)
as some kind of splicer, resulting in seemingly almost-universal container converter:
-- (<|>) = 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
Indeed, I was able to generalize all functions from Data.List
, here's some:
-- 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)
Also, this analogy makes some interesting new instances, such as a working applicative functor for functor sums (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)
Is it actually good idea to generalize all functions and make new instances with this analogy, especially for list operations?
EDIT: I'm especially concerned about ambiguous return type. For normal list operations, the return type is deducible from its argument types. But the "universal" version is not, as the return type must be explicitly specified. Is this problem severe enough to regard the analogy dangerous? (Or is there any other problem?)
EDIT 2: If I'm understanding the behavior of foldl'
exactly, the time complexity of universal splitAt
(shown above) must be Θ(length xs)
, as foldl'
is strict for every elements, right? If yes, that must be a problem, as it's inferior to the regular version's Θ(min n (length xs))
.