如何让GHCi告知`allEqual <*> reverse`中使用的Applicative实例?
函数式编程问题:确定
allEqual <*> reverse使用的Applicative实例 GHCi交互记录
> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b > :t allEqual allEqual :: Eq a => [a] -> [a] -> Bool > :t allEqual <*> reverse allEqual <*> reverse :: Eq a => [a] -> Bool > :t reverse reverse :: [a] -> [a]
问题
我想要确定allEqual <*> reverse中具体使用的Applicative实例是什么。我尝试进行推理:
f (a -> b) :: [a] -> [a] -> Bool f a :: [a] -> [a] f b :: [a] -> Bool
但这个推理并不合理,因为f b与a相关。请问有没有办法让GHCi直接告知此处实际使用的Applicative实例f是什么?如果没有的话,Haskell开发者通常是如何推理这类问题的?
解答
让GHCi验证Applicative实例的方法
GHCi没有直接命令输出具体的Applicative实例,但可以通过类型约束验证的方式确认:
- 开启类型应用扩展,手动指定实例类型进行验证:
> :set -XTypeApplications > :t allEqual <*> reverse :: Eq a => (-> [a]) Bool allEqual <*> reverse :: Eq a => [a] -> Bool
如果没有报错,说明(->) [a](函数类型的Applicative实例,即Reader实例)是正确的。
2. 开启详细类型打印,查看隐含约束:
> :set -fprint-explicit-foralls -fprint-explicit-kinds > :t allEqual <*> reverse allEqual <*> reverse :: forall a. Eq a => [a] -> Bool
虽然不会直接写出f的类型,但结合推导可以确认实例。
手动推理的思路
这里使用的是函数类型(->) r的Applicative实例,推导步骤如下:
- 把
allEqual的类型改写为Eq a => (->) [a] ([a] -> Bool),对应Applicative类型签名里的f (x -> y),其中f = (->) [a],x -> y = [a] -> Bool。 - 把
reverse的类型改写为(->) [a] [a],对应Applicative签名里的f x,其中x = [a]。 - 函数类型的
<*>实现逻辑是:(<*>) f g x = f x (g x),也就是将同一个输入x传给两个函数,用前者的输出(函数)调用后者的输出。对应到本例就是\x -> allEqual x (reverse x),最终类型为[a] -> Bool,和GHCi的输出完全匹配。
简单总结:当<*>两侧都是函数类型时,几乎必然使用的是(->) r这个Applicative实例,它的作用是组合两个共享输入的函数,生成新函数。
内容的提问来源于stack exchange,提问作者Tom Huntington
相关产品推荐
相关产品推荐

