我正在尝试使用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.
看起来很好。无法从某种类型构造函数和某种具体类型中“分解”/“构造”(可能不是此处使用的正确术语)类型3
的值Num
。
回到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>
这里发生的微妙之处是什么?
答案 0 :(得分:6)
这是工作中可怕的单形态限制。以下编译正常:
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我相信它是。这就是为什么问题不会在那里出现的原因。
答案 1 :(得分:1)
您的val3
是通用的。它是Kung t a
类型的通用值,其中t
和a
尚未知晓。 GHCi接受它很好,因为它可以保持它并等到你提供混凝土t
和a
。事实上,只要您尝试使用该值(通过打印出来)而不提供类型,GHCi就会对您不满。
但GHC无法承担&#34;坚持&#34;:它需要知道类型才能完成编译。
您可以通过明确告诉编译器您希望自己拥有一个通用值来解决这种情况,以后可能会由愿意提供合适类型的消费者使用。为此,请使用类型注释:
val3 :: Num (t a) => Kung t a
val3 = Kung { field = 3 }
在幕后,这样的定义将被编译为一个函数,它接受字典Num (t a)
并返回类型为Kung t a
的值。
回答问题&#34; GHCi如何设法&#34;分解&#34; /&#34;解构&#34;价值3 &#34; (我在这里添加了这个答案,但我不确定这是否是您所要求的)。
数字文字在Haskell中也是多态的。当您编写3
时,编译器会将其理解为fromInteger (3::Integer)
,其中fromInteger
是Num
类中的函数。这意味着,理论上,文字3
可以包含任何类型,只要该类型定义了Num
实例。
因此,当您编写Kung { field = 3 }
内容时,编译器将其视为Kung { field = fromInteger 3 }
,并且这很可能是任何类型Kung t a
,如果只有编译器可以证明存在类型Num
的{{1}}实例,可用于将t a
转换为3
。