例如,
-- 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]
对于三个示例,<*>
显示了不同的行为。怎么了?为什么在第三种情况下,它需要[Char] -> [Char]
而不是[Char]
,就像第一种情况一样。更重要的是,即使元组中只有[Char]
,<*>
也将它们组合在一起。
答案 0 :(得分:4)
不同之处在于列表是同类,而元组则不是:列表只包含相同类型的元素,而元组则不包含。
即使没有看应用程序,仿函数已经显示出主要区别:
fmap succ [1,2,3] ==> [2,3,4]
fmap succ ("a", 4) ==> ???
认为fmap
将succ
应用于"a"
是不合逻辑的。会发生什么是只有第二个组件受到影响:
fmap succ ("a", 4) ==> ("a", 5)
确实,看看实例:
instance Functor [] where ...
instance Functor ((,) a) where ...
请注意a
类型。在列表实例中,[]
只接受一个类型参数,并且该类型受fmap
影响。在(,)
中,我们有两个类型参数:一个是固定的(到a
),在应用fmap
时不会改变 - 只有第二个参数。< / p>
请注意,当两个类型参数都被强制相同时,理论上可以允许Functor (,)
的实例。例如,
instance Functor (\b -> (b,b)) where ...
但是Haskell不允许这样做。如果需要,需要一个newtype包装器:
newtype P b = P (b,b)
instance Functor P where
fmap f (P (x,y)) = P (f x, f y)
答案 1 :(得分:1)
Applicative是满足applicative laws的pure
和<*>
的数据类型和定义的任意组合:
[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
这些法律确保<*>
的行为与功能应用非常相似,但是某种“特殊情境”取决于应用。对于Maybe
的情况,上下文可能缺乏价值。对于元组,上下文是“伴随每个值的单一注释”。
pure
和<*>
可以为不同的数据类型做很多不同的事情,只要它们遵守法律。
实际上,相同的数据类型可以以不同的方式应用。列表具有<*>
“获取所有组合”的Applicative实例,但也包含使用辅助ZipList
newtype实现的实例,其中<*>
拉链列表并且pure
构造无限名单。