您可以使用
-ddump-ds
让GHC向您展示它正在使用的实例,如下所示:
GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help
ghci> :{
ghci| allEqual :: Eq a => [a] -> [a] -> Bool
ghci| allEqual = undefined -- or your actual definition
ghci| :}
ghci> :set -ddump-ds -dsuppress-all -dno-suppress-type-applications
ghci> x = allEqual <*> reverse
==================== Desugared ====================
letrec {
x_aR1
= \ @a_aSP $dEq_aT1 ->
let { $dEq_aSQ = $dEq_aT1 } in
let { $dApplicative_aSL = $fApplicativeFUN @[a_aSP] } in
letrec {
x_aSW
= <*>
@((->) [a_aSP])
$dApplicative_aSL
@[a_aSP]
@Bool
(allEqual @a_aSP $dEq_aSQ)
(reverse @a_aSP); } in
x_aSW; } in
returnIO
@[Any]
(: @Any
(unsafeCoerce#
@LiftedRep
@LiftedRep
@(forall {a}. Eq a => [a] -> Bool)
@Any
x_aR1)
([] @Any))
ghci>
这个
@((->) [a_aSP])
之后
<*>
在输出中意味着它正在使用函数的实例(列为
Applicative ((->) r)
中的实例
the documentation
).
或者,你可以在没有GHC帮助的情况下,通过查看类型来解决问题,如下所示:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
allEqual :: Eq a => [a] -> [a] -> Bool
reverse :: [a] -> [a]
为了简单起见,我将对后两种类型进行阿尔法转换,以不重用
a
类型变量:
allEqual :: Eq t => [t] -> [t] -> Bool
reverse :: [t] -> [t]
记住这一点
->
是右联想的,也可以用前缀的形式写,所以
[t] -> [t] -> Bool
与相同
(->) [t] ([t] -> Bool)
自从
allEqual
是的第一个参数
<*>
,其类型必须与统一
f (a -> b)
类似地,作为第二个参数,的类型
reverse
必须与统一
f a
.当你把所有这些都插进去时,你就会明白
f
必须是
(->) [t]
,
一
必须是
[t]
和
b
必须是
Bool
。一个快速的健全性检查是正确的,就是观察
f b
是的
[t] -> Bool
,在阿尔法等价之后,它是推断为的输出的相同类型GHC
allEqual <*> reverse
自从
f
是什么
Applicative
上需要实例,它必须将该实例用于
(->)[t]
(将列表作为参数的函数)。正在执行
:instances ((->) [t])
会给你看
instance Applicative ((->) [t]) -- Defined in âGHC.Baseâ
,但实际使用的实例更通用,适用于任何类型的函数。找到功能箭头后,执行
:info (->)
将向您展示:
instance Applicative ((->) r) -- Defined in âGHC.Baseâ
.