Haskell中Applicative的*>实现:类型匹配与列表转换疑问
关于Haskell中Applicative实例
(*>)实现的疑问 我正在通过fp-course的练习学习Haskell,在填充Application.hs中的(*>)函数时,看不懂给出的答案:
-- | Apply, discarding the value of the first argument. -- Pronounced, right apply. -- -- >>> (1 :. 2 :. 3 :. Nil) *> (4 :. 5 :. 6 :. Nil) -- [4,5,6,4,5,6,4,5,6] -- -- >>> (1 :. 2 :. Nil) *> (4 :. 5 :. 6 :. Nil) -- [4,5,6,4,5,6] -- -- >>> (1 :. 2 :. 3 :. Nil) *> (4 :. 5 :. Nil) -- [4,5,4,5,4,5] -- -- >>> Full 7 *> Full 8 -- Full 8 -- -- prop> \a b c x y z -> (a :. b :. c :. Nil) *> (x :. y :. z :. Nil) == (x :. y :. z :. x :. y :. z :. x :. y :. z :. Nil) -- -- prop> \x y -> Full x *> Full y == Full y (*>) :: Applicative k => k a -> k b -> k b (*>) ka kb = (pure id) <$> ka <*> kb
以k为List为例,我有两个疑问:
- 为何
<$>的第一个参数类型(a -> a)能够接受类型为List (a -> a)的pure id? - 我不理解为何
(pure id) <$> ka能将1 :. 2 :. 3 :. Nil转换为id :. id :. id :. Nil?
解答
疑问1的澄清
你混淆了<$>的参数类型和作用:
<$>(即fmap)的类型是Functor f => (x -> y) -> f x -> f y,它的第一个参数必须是普通函数,而非Functor容器值。pure id的类型是Applicative k => k (b -> b),是一个容器值,直接把它作为<$>的第一个参数不符合类型要求——这说明你看到的代码大概率存在笔误,正确的实现应该是用const id(类型为a -> (b -> b)的函数,接受任意a类型值,返回id函数)替代pure id,即:
如果你看到的代码确实是(*>) ka kb = (const id) <$> ka <*> kb(pure id) <$> ka,那可能是把<$>和<$搞混了——x <$ y是const x <$> y的语法糖,(pure id) <$ ka等价于const (pure id) <$> ka,此时第一个参数是const (pure id),类型为a -> k (b -> b),符合<$>的要求。
疑问2的解释
如果是正确的const id <$> ka:
const id的作用是忽略输入值,直接返回id函数。对于List类型来说,const id <$> (1 :. 2 :. 3 :. Nil)会把列表中的每个元素都替换成id函数,最终得到id :. id :. id :. Nil。- 之后
<*> kb会把每个id函数应用到kb的所有元素上,对于List的Applicative实例来说,就是做笛卡尔积运算,最终得到kb重复ka长度次的结果,和示例输出一致。
如果是你看到的(pure id) <$> ka:
- 它的结果会是
(pure id) :. (pure id) :. (pure id) :. Nil,也就是[[id], [id], [id]](List嵌套List的函数容器),之后<*> kb虽然也能得到示例中的结果,但这不是(*>)的常规实现方式,且类型推导上需要额外的层级处理,显然不合理。
内容的提问来源于stack exchange,提问作者zichao liu
相关产品推荐
相关产品推荐

