GHCi 如何打印从 "pure" 创建的部分应用的值?

How does GHCi print partially-applied values created from "pure"?

我一直在研究 Applicative 个实例以弄清楚它们是如何工作的。但是,老实说,我不理解这种行为。

如果我定义自己的数据类型,然后在没有其他参数的情况下将 pure 应用于它,则不会打印任何内容,但如果我尝试将某些内容应用于结果,则会出错。

ghci> data T = A
ghci> pure A
ghci> pure A 0

<interactive>:21:1:
    No instance for (Show T) arising from a use of ‘print’
    In a stmt of an interactive GHCi command: print it

但是,如果我使 T 成为 Show 的实例,那么在这两种情况下都会打印出 A

ghci> data T = A deriving (Show)
ghci> pure A
A
ghci> pure A 0
A

我真的不明白 pure A 怎么会是一个在两种情况下打印不同的值。 pure A不是部分应用了吗?

我确实理解为什么在第一个示例中调用 pure A 0 错误而在第二个示例中却没有——这对我来说很有意义。那是使用 Applicative((->) r) 实例,所以它简单地产生一个总是 returns A.

的函数

但是,当应用程序本身的类型尚不清楚时,pure 是如何仅用一个值实例化的呢?此外,GHC 怎么可能打印这个值?

当您为 GHCi 提示提供一个模糊类型的值进行评估时,它会尝试以各种方式默认该类型。特别是,它会尝试它是否适合 IO a 类型,以防您要执行 IO 操作(请参阅 the GHC manual)。在您的例子中,pure A 默认为 IO T 类型。还有:

Furthermore, GHCi will print the result of the I/O action if (and only if):

  • The result type is an instance of Show.
  • The result type is not ().

GHCi 有点奇怪。特别是,当您在提示符下键入表达式时,它会尝试以两种不同的方式解释它,顺序为:

  1. 作为要执行的 IO 操作。
  2. 作为要打印的值。

因为 IOApplicative,它将 pure A 解释为一个 IO 动作产生类型 T。它执行该操作(什么都不做),并且由于结果不在 Show 中,因此它不会打印任何内容。如果你使 T 成为 Show 的实例,那么它会为你打印出结果。

当你写 pure A 0 时,GHCi 会看到:

pure :: Applicative f => a -> f a
pure A :: Applicative f => f T

并且由于您将 pure A 应用于 0,因此 pure A 必须是某些类型 ab 的函数 a->ba 必须包含 0.

(Num a, Applicative f) => f T ~ (a -> b)

(注意x ~ y表示xy统一——可以使它们具有相同的类型。)

因此我们必须有 f ~ ((->) a)T ~ b,所以实际上 GHC 推断,在这种情况下,

pure A :: Num a => ((->) a) T

我们可以改写为

pure A :: Num a => a -> T

嗯,(->) aApplicative的一个实例,即"reader",这样就可以了。当我们将 pure A 应用于 0 时,我们得到类型 T 的东西,即 A。这个 不能 被解释为一个 IO 动作,当然,所以如果 T 不是 Show 的实例,GHCi 会抱怨。