Haskell 为什么GHCi在提示下接受赢得';不编译?

Haskell 为什么GHCi在提示下接受赢得';不编译?,haskell,ghci,Haskell,Ghci,我尝试使用Haskell类型,创建一个数据类型构造函数和一个具体类型(灵感来源于) 这是我的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\Hask

我尝试使用Haskell类型,创建一个数据类型构造函数和一个具体类型(灵感来源于)

这是我的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.
看起来不错。无法“分解”/“构造”(可能不是这里使用的正确术语)来自某个类型构造函数和某个具体类型的类型
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人为地“管理”分解
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>
*Main>val3
:50:1:错误:
*由于使用“print”而产生的类型变量“t0”和“a0”不明确
阻止解决约束“(显示(t0 a0))”。
可能的修复:使用类型注释指定't0','a0'应该是什么。
这些潜在的例子存在:
实例(Show b,Show a)=>Show(或a或b)
--在“Data.other”中定义
实例[safe]Show(ta)=>Show(ta)
--定义于C:\Test\Haskell\kung.hs:1:49
实例Show a=>Show(可能是a)--在“GHC.Show”中定义
…再加上15个人
…加上一个涉及范围外类型的实例
(使用-fprint潜在实例查看所有实例)
*在交互式GHCi命令的stmt中:打印它
*Main>

这里发生的微妙之处是什么

您的
val3
是通用的。它是
a
类型的通用值,其中
t
a
尚不清楚。GHCi很好地接受它,因为它可以抓住它,等待您提供混凝土
t
a
。事实上,只要您尝试使用该值(通过打印出来)而不提供类型,GHCi就会向您求助

但GHC不能“坚持”:它需要知道类型才能完成编译

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

val3 :: Num (t a) => Kung t a
val3 = Kung { field = 3 }
在幕后,这样的定义将被编译成一个函数,它接受一个dictionary
Num(ta)
,并返回一个类型为
Kung t a
的值


回答“GHCi如何“分解”/“解构”价值3”的问题(我在这里添加了这个答案,但我不确定这是否是您要问的)

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


因此,当您编写某个
Kung{field=3}
时,编译器会将其视为
Kung{field=fromInteger 3}
,这很可能是任何类型的
Kung t a
,只要编译器能够证明类型
ta
有一个
Num
实例,它可以用来将
3
转换为
ta

您的
val3
是通用的。它是
a
类型的通用值,其中
t
a
尚不清楚。GHCi很好地接受它,因为它可以抓住它,等待您提供混凝土
t
a
。事实上,只要您尝试使用该值(通过打印出来)而不提供类型,GHCi就会向您求助

但GHC不能“坚持”:它需要知道类型才能完成编译

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

val3 :: Num (t a) => Kung t a
val3 = Kung { field = 3 }
在幕后,这样的定义将被编译成一个函数,它接受一个dictionary
Num(ta)
,并返回一个类型为
Kung t a
的值


回答“GHCi如何“分解”/“解构”价值3”的问题(我在这里添加了这个答案,但我不确定这是否是您要问的)

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


因此,当您编写某个
Kung{field=3}
时,编译器会将其视为
Kung{field=fromInteger 3}
,这很可能是任何类型的
Kung t a
,只要编译器能够证明类型
ta
有一个
Num
实例,它可以用它把
3
转换成
ta

这就是工作中可怕的单态性限制。以下内容很好:

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

val3 :: Num (t a) => Kung t a
val3 = Kung { field =  3 }
但是,该方法阻止GHC推断该签名本身。相反,它试图找到一种单态类型。为此,它只有Haskell默认规则可用。通常,这意味着受
Num
约束的类型变量是单态的
Integer
。。。但整数的形式不是
ta
,因此此操作失败

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