为什么 GHCi 在提示符下接受一些无法编译的东西?

Why is GHCi accepting something at the prompt that won't compile?

我正在尝试使用 Haskell 类型,创建一个采用类型构造函数和具体类型的数据(受 this 启发)。

这是我的 kung.hs 文件:

data Kung t a = Kung { field :: t a } deriving (Show, Eq)

val1 = Kung { field = [1,5] }

val2 = Kung { field =  Just 3 }

--val3 = Kung { field =  3 }

编译正常,加载正常:

*Main> :load C:\Test\Haskell\kung.hs
[1 of 1] Compiling Main             ( C:\Test\Haskell\kung.hs, interpreted )
Ok, one module loaded.
*Main> val1
Kung {field = [1,5]}
*Main> val2
Kung {field = Just 3}
*Main>

现在相同的版本,但取消注释 val3:

data Kung t a = Kung { field :: t a } deriving (Show, Eq)

val1 = Kung { field = [1,5] }

val2 = Kung { field =  Just 3 }

val3 = Kung { field =  3 }

这不编译:

*Main> :load C:\Test\Haskell\kung.hs
[1 of 1] Compiling Main             ( C:\Test\Haskell\kung.hs, interpreted )

C:\Test\Haskell\kung.hs:7:24: error:
    * No instance for (Num (t0 a0)) arising from the literal `3'
    * In the `field' field of a record
      In the expression: Kung {field = 3}
      In an equation for `val3': val3 = Kung {field = 3}
  |
7 | val3 = Kung { field =  3 }
  |                        ^
Failed, no modules loaded.

这看起来不错。无法从某些类型构造函数和某些具体类型 "decompose" / "construct" (可能不是此处使用的正确术语)类型 Num 的值 3

回到 GHCi 解释器,加载没有 val3 注释的文件的第一个版本,然后:

Prelude> :load C:\Test\Haskell\kung.hs
[1 of 1] Compiling Main             ( C:\Test\Haskell\kung.hs, interpreted )
Ok, one module loaded.
*Main> val3 = Kung { field =  3 }
*Main> :t val3
val3 :: Num (t a) => Kung t a

我应该怎么理解?为什么GHCi要人为"manage"分解3? (不给出真实类型)

那么这个 val3 似乎并不可行:

*Main> val3

<interactive>:50:1: error:
    * Ambiguous type variables `t0', `a0' arising from a use of `print'
      prevents the constraint `(Show (t0 a0))' from being solved.
      Probable fix: use a type annotation to specify what `t0', `a0' should be.
      These potential instances exist:
        instance (Show b, Show a) => Show (Either a b)
          -- Defined in `Data.Either'
        instance [safe] Show (t a) => Show (Kung t a)
          -- Defined at C:\Test\Haskell\kung.hs:1:49
        instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
        ...plus 15 others
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    * In a stmt of an interactive GHCi command: print it
*Main>

这里发生了什么微妙的事情?

您的 val3 是通用的。它是 Kung t a 类型的通用值,其中 ta 尚不清楚。 GHCi 可以很好地接受它,因为它可以保留它并等到您提供具体的 ta。事实上,一旦您尝试在不提供类型的情况下使用该值(通过打印出来),GHCi 就会放弃。

但是 GHC 负担不起"hold on":它需要知道类型才能完成编译。

您可以通过明确告诉编译器您希望自己拥有一个泛型值来补救这种情况,该值稍后可以由愿意提供合适类型的消费者使用。为此,请使用类型注释:

val3 :: Num (t a) => Kung t a
val3 = Kung { field = 3 }

在幕后,这样的定义将被编译为一个函数,该函数接受字典 Num (t a) 和 returns 类型 Kung t a.

的值

回答问题“GHCi 如何设法 "decompose"/"deconstruct" 值 3”(我在这里添加这个答案,但是我不确定你问的是不是这个)。

数字文字在 Haskell 中也是多态的。当您编写 3 时,编译器会将其理解为 fromInteger (3::Integer),其中 fromInteger 是来自 Num class 的函数。这意味着,理论上,文字 3 可以有任何类型,只要该类型定义了 Num 实例。

所以当你写一些东西 Kung { field = 3 } 时,编译器将其视为 Kung { field = fromInteger 3 },这很可能是任何类型 Kung t a,只要编译器能够证明存在是类型 t aNum 实例,可用于将 3 转换为 t a

这就是可怕的单态限制在起作用。以下编译正常:

data Kung t a = Kung { field :: t a } deriving (Show, Eq)

val3 :: Num (t a) => Kung t a
val3 = Kung { field =  3 }

然而,monomorphism restriction 阻止 GHC 自己推断此签名。相反,它试图找到一个 单态 类型。为此,它只有 Haskell 默认规则 可用。通常,这些意味着 Num-约束类型变量被单态化为 Integer...但是整数不是 t a 的形式,所以这失败了。

正确的解决方法确实是自己编写类型签名,但您也可以关闭单态限制:

{-# LANGUAGE NoMonomorphismRestriction #-}

data Kung t a = Kung { field :: t a } deriving (Show, Eq)

val3 = Kung { field =  3 }

在 GHCi 中,默认情况下单态限制是关闭的,因为我相信是 GHC-7.8。这就是为什么那里没有出现问题的原因。