Haskell中Applicative在元组和列表上的不同行为

yua*_*ili 4 haskell applicative

例如,

-- 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]
Run Code Online (Sandbox Code Playgroud)

对于三个示例,<*>显示了不同的行为。怎么了?为什么在第三种情况下,它期望一个[Char] -> [Char]而不是[Char]在第一种情况下。而且,即使只有[Char]元组,也可以<*>将它们组合在一起。

chi*_*chi 5

不同之处在于列表是齐次的,而元组则不是:列表仅包含相同类型的元素,而元组则不必。

即使不看任何应用,函子也已经显示出主要区别:

fmap succ [1,2,3]  ==> [2,3,4]
fmap succ ("a", 4) ==> ???
Run Code Online (Sandbox Code Playgroud)

争论fmap适用succ于事实是不合逻辑的"a"。发生的是只有第二个组件受到影响:

fmap succ ("a", 4) ==> ("a", 5)
Run Code Online (Sandbox Code Playgroud)

实际上,请查看实例:

instance Functor [] where ...
instance Functor ((,) a) where ...
Run Code Online (Sandbox Code Playgroud)

注意a类型。在list实例中,[]仅接受一个type参数,而该类型是受所影响的类型fmap。在其中,(,)我们有两个类型参数:一个是固定的(to a),并且在应用时不会更改fmap-只有第二个是固定的。

注意,从理论上讲,有可能接受Functor (,)两个类型参数被强制相同的实例。例如,

instance Functor (\b -> (b,b)) where ...
Run Code Online (Sandbox Code Playgroud)

但是Haskell不允许这样做。如果需要,则需要一种新型包装器:

newtype P b = P (b,b)
instance Functor P where
   fmap f (P (x,y)) = P (f x, f y)
Run Code Online (Sandbox Code Playgroud)