pure (+) <*> (Just 1) <*> (Just 2)
上面表达式的展开是否正确?
pure (+) <*> (Just 1) <*> (Just 2)
= (Just (+)) <*> (Just 1) <*> (Just 2)
= (Just (1+)) <*> (Just 2)
= Just 3
如果是这样,那为什么我们不能这样做
(Just (+)) <*> (Just 2)
?
相信您已经在评论中回答了您的问题。所以只是为了完成......
(Just (+)) <*> (Just 2)
是 REPL 无法打印的部分应用函数,并给出错误:
• No instance for (Show (Integer -> Integer)) arising from a use of ‘print’
,
但除此之外还好。
这里没问题:
> :t (Just (+)) <*> (Just 2)
(Just (+)) <*> (Just 2) :: Num a => Maybe (a -> a)
.
完成功能也不错:
> f = (Just (+)) <*> (Just 2)
> f <*> Just 1
> Just 3
你的理解似乎完全正确。
顺便说一句,另一种写法是:
(+) <$> (Just 1) <*> (Just 2)
扩展确实是正确。表情
pure (+) <*> Just 1 <*> Just 2
相当于:
(+) <$> Just 1 <*> Just 2
和:
liftA2 (+) (Just 1) (Just 2)
不仅适用于
Maybe
,还适用于任何 Applicative
类型。
“机械”扩展是首先查看表达式,这里是表达式
pure (+) <*>1 Just 1 <*>2 Just 2
相当于:
(<*>2) ((<*>1) (pure (+)) (Just 1)) (Just 2)
我添加了下标来跟踪
<*>
的两种用法。此时我们不知道 Applicative
实例 pure
会“选择”什么,但这将根据“类型传播”来确定。
外部
(<*>)
,因此 (<*>2)
具有类型 Applicative f => f (a -> b) -> f a -> f b
,对于内部类型,我们选择 (<*>1) :: Applicative g => g (c -> d) -> g c -> g d
。两者中的第二个操作数是 Num m => Maybe m
和 Num n => Maybe n
。因此,这意味着 f
和 g
都将与 f ~ g ~ Maybe
匹配。
这也意味着
pure
将采用 Maybe
的 Applicative
实例,这意味着 pure
相当于 Just
,因此在这种情况下 pure (+)
将是 Num o => Just (o -> o -> o)
。如果我们将其与内部 (<*>)
的类型相匹配,则表示 o ~ m
,而与外部 (<*>)
的类型相匹配,则表示 o ~ m ~ n
。所以表达式的类型将是:
pure (+) <*> Just 1 <*> Just 2 :: Num m => Maybe m
而对于扩展,我们可以将
pure
替换为Just
,所以:
Just (+) <*> Just 1 <*> Just 2
然后
Just (+) <*> Just 1
将转换为 Just (1 +)
,因为两个操作数都是 Just
,并且操作数实现为:
instance Applicative Maybe where pure = Just Just f <*> m = fmap f m Nothing <*> _m = Nothing
最后
Just (1 +) <*> Just 2
,工作原理相同,所以:
Just (1 + 2)
相当于:
Just 3
如果是这样,那为什么我们不能这样做
。(Just (+)) <*> (Just 2)
我们可以,我们只是不能
show
结果。 Just (+) <*> Just 2
的结果将是Just (2 +)
,所以这是一个函数,并且函数不能显示(默认情况下)。
但是我们可以这样做:
ghci> Just (1 +) <*> Just 2
Just 3
或者我们可以使用
Just (+) <*> Just 2
的结果来处理第二个操作数:
ghci> (Just (+) <*> Just 2) <*> Just 3
Just 5
括号在这里不是必需的,但用于使表达式的解读方式更加明确。