问题
For example,
-- Num a => ([Char], a -> a) <*> ([Char], a)
> ("hello ",(*6)) <*> ("world",7)
("hello world",42)
-- Num a => [a -> a] <*> [a]
> [(*7),(*6)] <*> [6,7]
[42,49,36,42]
-- Num a => [[Char], a -> a] <*> [[Char], a]
> ["hello ",(*6)] <*> ["world",7]
<interactive>:17:2:
Couldn't match expected type ‘[Char] -> [Char]’
with actual type ‘[Char]’
In the expression: "hello "
In the first argument of ‘(<*>)’, namely ‘["hello ", (* 6)]’
In the expression: ["hello ", (* 6)] <*> ["world", 7]
For three examples, <*>
shows different behaviors. What happens? Why in the third case, it expects a [Char] -> [Char]
rather than [Char]
just like in the first case. What's more, even there's only [Char]
in tuples, <*>
combines them together.
回答1:
The difference lies in the fact that lists are homogeneous, while tuple are not: lists contain only elements of the same type, while tuples do not have to.
Even without looking at applicatives, functors already show a main difference:
fmap succ [1,2,3] ==> [2,3,4]
fmap succ ("a", 4) ==> ???
It would be illogical to argue that fmap
applies succ
to "a"
. What happens is that only the second component gets affected:
fmap succ ("a", 4) ==> ("a", 5)
Indeed, look at the instances:
instance Functor [] where ...
instance Functor ((,) a) where ...
Notice the a
type. In the list instance, []
takes only one type parameter, and that's the type affected by fmap
. In (,)
we have two type parameters: one is fixed (to a
) and does not change when applying fmap
-- only the second one does.
Note that it would be theoretically possible to admit an instance of Functor (,)
when both type arguments are forced to be the same. E.g.,
instance Functor (\b -> (b,b)) where ...
but Haskell does not allow this. If needed, one needs a newtype wrapper:
newtype P b = P (b,b)
instance Functor P where
fmap f (P (x,y)) = P (f x, f y)
回答2:
An Applicative is whatever combination of a datatype and of definitions of pure
and <*>
that satisfies the applicative laws:
[identity] pure id <*> v = v
[composition] pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
[homomorphism] pure f <*> pure x = pure (f x)
[interchange] u <*> pure y = pure ($ y) <*> u
These laws ensure that <*>
behaves very much like function application, but one taking place in some kind of "special context" that depends on the Applicative. For the case of Maybe
the context is the possible absence of value. For tuples, the context is "monoidal annotations that accompany each value".
pure
and <*>
can do very different things for different datatypes as long as they respect the laws.
In fact, the same datatype can be an Applicative in different ways. Lists have the Applicative instance in which <*>
"obtains all combinations", but also the instance implemented with the auxiliary ZipList newtype, where <*>
zips lists together and pure
constructs an infinite list.
来源:https://stackoverflow.com/questions/33852781/different-behaviors-of-applicative-on-tuples-and-lists-in-haskell