ghci 可以告诉我们正在使用哪个类的实例吗

Tom*_*ton 5 haskell

这是一些 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 如何推理这些东西。

Jos*_*ica 7

您可以使用-ddump-dsGHC 来显示它正在使用哪个实例,如下所示:

\n
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> \n
Run Code Online (Sandbox Code Playgroud)\n

输出中的后面@((->) [a_aSP])<*>表示它正在使用函数实例(Applicative ((->) r)文档中列为实例)。

\n

或者,您可以在没有 GHC 帮助的情况下仅通过查看类型来弄清楚,如下所示:

\n
(<*>) :: Applicative f => f (a -> b) -> f a -> f b\nallEqual :: Eq a => [a] -> [a] -> Bool\nreverse :: [a] -> [a]\n
Run Code Online (Sandbox Code Playgroud)\n

为了简单起见,我将对后两种类型进行 alpha 转换,以不重用a类型变量:

\n
allEqual :: Eq t => [t] -> [t] -> Bool\nreverse :: [t] -> [t]\n
Run 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

\n


chi*_*chi 7

我有时使用的一个快速技巧是添加一个键入的孔,如 中所示(polymorphicValue `asTypeOf` _)

更准确地说,asTypeOf函数定义为

asTypeOf :: t -> t -> t
asTypeOf x y = x
Run Code Online (Sandbox Code Playgroud)

它与 非常相似const,但它强制其两个参数xy具有相同的类型。

_在表达式中间添加一个类型化的洞会告诉 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)