这是一些 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]
Run Code Online (Sandbox Code Playgroud)
我正在尝试弄清楚 中应用程序的实例到底是什么allEqual <*> reverse。
我试图推理
f (a -> b) :: [a] -> [a] -> Bool
f a :: [a] -> [a]
f b :: [a] -> Bool
Run Code Online (Sandbox Code Playgroud)
但这没有意义,因为与f b相关a。
无论如何,他们是想让 ghci 告诉我f这里实际使用的是什么应用程序。如果不是,haskellers 如何推理这些东西。
您可以使用-ddump-dsGHC 来显示它正在使用哪个实例,如下所示:
GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help\nghci> :{\nghci| allEqual :: Eq a => [a] -> [a] -> Bool\nghci| allEqual = undefined -- or your actual definition\nghci| :}\nghci> :set -ddump-ds -dsuppress-all -dno-suppress-type-applications\nghci> x = allEqual <*> reverse\n\n==================== Desugared ====================\nletrec {\n x_aR1\n = \\ @a_aSP $dEq_aT1 ->\n let { $dEq_aSQ = $dEq_aT1 } in\n let { $dApplicative_aSL = $fApplicativeFUN @[a_aSP] } in\n letrec {\n x_aSW\n = <*>\n @((->) [a_aSP])\n $dApplicative_aSL\n @[a_aSP]\n @Bool\n (allEqual @a_aSP $dEq_aSQ)\n (reverse @a_aSP); } in\n x_aSW; } in\nreturnIO\n @[Any]\n (: @Any\n (unsafeCoerce#\n @LiftedRep\n @LiftedRep\n @(forall {a}. Eq a => [a] -> Bool)\n @Any\n x_aR1)\n ([] @Any))\n\n\nghci> \nRun Code Online (Sandbox Code Playgroud)\n输出中的后面@((->) [a_aSP])的<*>表示它正在使用函数实例(Applicative ((->) r)在文档中列为实例)。
或者,您可以在没有 GHC 帮助的情况下仅通过查看类型来弄清楚,如下所示:
\n(<*>) :: Applicative f => f (a -> b) -> f a -> f b\nallEqual :: Eq a => [a] -> [a] -> Bool\nreverse :: [a] -> [a]\nRun Code Online (Sandbox Code Playgroud)\n为了简单起见,我将对后两种类型进行 alpha 转换,以不重用a类型变量:
allEqual :: Eq t => [t] -> [t] -> Bool\nreverse :: [t] -> [t]\nRun Code Online (Sandbox Code Playgroud)\n记住->是右结合的,也可以写成前缀形式,所以[t] -> [t] -> Bool与 相同(->) [t] ([t] -> Bool)。由于allEqual是 的第一个参数<*>,因此其类型必须与 统一f (a -> b)。同样,作为第二个参数, 的类型reverse必须与 统一f a。当你将所有这些都插入时,你会发现f必须是(->) [t],a必须是[t],并且b必须是Bool。快速健全性检查这是正确的,方法是观察f bthen [t] -> Bool,在 alpha 等价之后,它与推断为 的输出的 GHC 类型相同allEqual <*> reverse。由于f这是Applicative需要实例的,因此它必须将实例用于(->) [t](采用列表作为参数的函数)。这样做:instances ((->) [t])会告诉你instance Applicative ((->) [t]) -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99,但实际使用的实例更通用,适用于任何一种功能。一旦找到功能箭头,执行操作:info (->)会显示:instance Applicative ((->) r) -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99。
我有时使用的一个快速技巧是添加一个键入的孔,如 中所示(polymorphicValue `asTypeOf` _)。
更准确地说,asTypeOf函数定义为
asTypeOf :: t -> t -> t
asTypeOf x y = x
Run Code Online (Sandbox Code Playgroud)
它与 非常相似const,但它强制其两个参数x和y具有相同的类型。
_在表达式中间添加一个类型化的洞会告诉 GHC 引发一个类型错误,告诉应该使用什么类型来填充该洞。
因此,(thing `asTypeOf` _)要求 GHC 打印 的类型thing,这在多态时非常有用thing。
让我们将其应用到您的案例中:
ghci> :t allEqual <*> reverse
allEqual <*> reverse :: Eq a => [a] -> Bool
Run Code Online (Sandbox Code Playgroud)
首先,我们转向(<*>)前缀表示法。
ghci> :t (<*>) allEqual reverse
(<*>) allEqual reverse :: Eq a => [a] -> Bool
Run Code Online (Sandbox Code Playgroud)
然后,我们插入键入的孔:
ghci> :t ((<*>) `asTypeOf` _) allEqual reverse
* Found hole:
_ :: ([a] -> [a] -> Bool) -> ([a] -> [a]) -> [a] -> Bool
Valid hole fits include
(<*>) :: forall (f :: * -> *) t u.
Applicative f =>
f (t -> u) -> f t -> f u
with (<*>) @((->) [a]) @[a] @Bool
Run Code Online (Sandbox Code Playgroud)
我们了解(<*>)ie 的具体实例化类型([a] -> [a] -> Bool) -> ([a] -> [a]) -> [a] -> Bool,以及如何获得该类型的解释。确实,部分
(<*>) @((->) [a]) @[a] @Bool
Run Code Online (Sandbox Code Playgroud)
显示<*>根据(->) [a]应用程序和类型使用的[a]和Bool。我们可以将其描述如下:
(<*>) :: Applicative f => f (t -> u) -> f t -> f u
where f ~ (->) [a]
t ~ [a]
u ~ Bool
Run Code Online (Sandbox Code Playgroud)