Different behaviors of Applicative on tuples and lists in Haskell

故事扮演 提交于 2020-03-01 06:05:22

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!