我如何理解Haskell类型构造函数的有效输入集?

时间:2016-09-16 15:09:01

标签: haskell functional-programming

警告:非常初学者的问题。

我目前陷入了Haskell书中关于代数类型的部分,我正在阅读,并且我遇到了以下示例:

data Id a =
  MkId a deriving (Eq, Show)

idInt :: Id Integer
idInt = MkId 10

idIdentity :: Id (a -> a)
idIdentity = MkId $ \x -> x

好的,等一下。我不完全理解idIdentity示例。书中的解释是:

  

这有点奇怪。类型Id接受参数和数据   构造函数MkId接受相应多态的参数   类型。因此,为了获得Id Integer类型的值,我们需要   将a -> Id a应用于Integer值。这会将a类型变量绑定到   Integer并在类型构造函数中应用(->),给我们   Id Integer。我们还可以构造一个MkId值作为标识   函数通过将a绑定到两个类型中的多态函数   和术语水平。

但是等等。为什么只有完全多态函数?我之前的理解是a可以是任何类型。但显然受约束的多态类型不起作用:(Num a) => a -> a在这里不起作用,而GHC错误表明只有完全多态的类型或"合格的类型" (不确定它们是什么)是有效的:

f :: (Num a) => a -> a
f = undefined

idConsPoly :: Id (Num a) => a -> a
idConsPoly = MkId undefined

Illegal polymorphic or qualified type: Num a => a -> a
Perhaps you intended to use ImpredicativeTypes
In the type signature for ‘idIdentity’:
  idIdentity :: Id (Num a => a -> a)

编辑:我是傻瓜。正如@chepner在下面的回答中指出的那样,我在下面写错了类型签名。这也解决了我在下面的句子中的困惑......

回想起来,这种行为是有道理的,因为我还没有为Num定义Id个实例。但是,是什么原因解释了我能够在Integer中应用类似idInt :: Id Integer的类型?

所以一般来说,我想我的问题是:类型构造函数的有效输入集是什么?只有完全多态的类型?什么是#34;合格类型"然后?等...

3 个答案:

答案 0 :(得分:3)

您只是将类型构造函数放在错误的位置。以下情况很好:

idConsPoly :: Num a => Id (a -> a)
idConsPoly = MkId undefined

这里的类型构造函数Id* -> *种类,这意味着您可以为其提供任何具有*种类的值(包括所有“普通”类型)并返回一个新值善良*。一般来说,你更关心箭头处理函数(?),其中类型构造函数只是一个例子。

TypeProd是一个三元类型的构造函数,其前两个参数类型为* -> *

-- Based on :*: from Control.Compose
newtype TypeProd f g a = Prod { unProd :: (f a, g a) }

Either Int是一个表达式,其值具有类* -> *但不是类型构造函数,是类型构造函数Either到nullary类型构造函数Int的部分应用程序

答案 1 :(得分:1)

您的混淆也导致您误解了GHC的错误消息。这意味着“getLogger()是一种多态或合格的类型,因此非法(在此上下文中)”。

你从这本书中引用的最后一句话措辞不是很好,也许可能导致了这种误解。重要的是要意识到在Num a => a -> a中,参数Id (a -> a) 不是一个多态类型,而只是一个碰巧提到类型变量的普通类型。多态的是a -> a,对于任何类型idIdentity都可以有Id (a -> a)类型。

在标准的Haskell多态中,限定只能出现在类型签名中类型的最外层。

答案 2 :(得分:0)

类型签名几乎是正确的

idConsPoly :: (Num a) => Id (a -> a)

应该是对的,虽然我手机上没有ghc来测试这个。 <子> 另外我认为你的问题相当广泛,因此我故意只回答这里的具体问题。