4 ms·
> is why generalising these operations is considered a good thing? > Is this operation happening in a future? or is on a list? You could argue that the type si
by rovolo 7y ago
> is why generalising these operations is considered a good thing?
> Is this operation happening in a future? or is on a list? You could argue that the type signature will let you know, but quite often it's inferred.
1) You are generalizing the input of a function to multiple types. Many of the functions you'd write wouldn't care whether you're using a List or a Future, they would accept either. It's just like declaring your Java method to take a Collection instead of an ArrayList.
2) You are getting more specific in the output of the function. If you write `filter`, it would be nice to get a `LinkedList` back if you pass in a `LinkedList`. With more common type systems, the best you'll be able to do writing the function once is a return type of `Collection` or `Iterable`.
Your method may perform horribly if a LinkedList is passed in instead of an ArrayList. If your operation really depends on a specific behavior the Functor doesn't specify, you should be using a different type than Functor.
- sweeneyrod 7y ago> Many of the functions you'd write wouldn't care whether you're using a List or a Future, they would accept either. I can't imagine a situation where that would be true, except if you're writing a monad library. You could say that you might want to interchange a list of futures for a future of lists, but that's two interdependent things being swapped not one.
- Silhouette 7y agoYou've made at least two comments now mentioning this idea of interchange, but there's more to all of this than just that. The Haskell standard libraries are full of generic functions that can be applied to many practically useful types as long as those types provide certain basic guarantees. For example, there is a useful little function replicate :: Int -> a -> Seq a that allows us to build a sequence of n of some value. But what if we don't have a raw value, but instead some Applicative wrapper around it, and instead of just getting a sequence of individually wrapped values, we want that Applicative structure around the sequence? That is, we want some function f that will give us f 3 (Just 1) == Just (fromList [1, 1, 1]) but f 3 Nothing == Nothing with a Maybe, and f 3 (Right 'x') == Right (fromList "xxx") but f 3 (Left "error message") == Left "error message" with an Either, and similarly for any other Applicative. Well, such a function also exists in Data.Sequence: replicateA :: Applicative f => Int -> f a -> f (Seq a) Because it works with any Applicative, it can just as well work out all of the possible outcomes when we roll three dice: replicateA 3 [1, 2, 3, 4, 5, 6] or repeat the action of any Monad three times: replicateA 3 $ putStrLn "Hello, world!" or repeat whatever behaviour some Applicative you haven't even written yet will have next week. The rabbit hole gets as deep as you like. For example, we can easily compose a variety of other standard functions to count how many ways there are to reach each possible total in that dice example: map (head &&& length) $ group $ sort $ fmap sum $ replicateA 3 [1, 2, 3, 4, 5, 6] -- [(3,1),(4,3),(5,6),(6,10), ...] Many of these functions have quite general types, for example sum working on any Foldable and fmap working on any Functor. Because a list is an Applicative and any Applicative is also a Functor, and since the individual results of the replicateA will be Seqs and any Seq is also a Foldable, everything works fine, and the list structure we started with is preserved all the way through.