【问题标题】:How do I understand the set of valid inputs to a Haskell type constructor?如何理解 Haskell 类型构造函数的有效输入集?
【发布时间】:2016-09-17 01:52:57
【问题描述】:

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

我目前正在阅读我正在阅读的 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可以是any类型。但显然受约束的多态类型不起作用:(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 在下面的回答中指出的那样,我错误地写了下面的类型签名。这也解决了我在下面的下一句中的困惑......

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

所以总的来说,我想我的问题是:类型构造函数的有效输入集具体是什么?只有完全多态的类型?那么什么是“合格类型”?等等……

【问题讨论】:

标签: haskell functional-programming


【解决方案1】:

你只是把类型构造器放在了错误的地方。以下是好的:

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 对空值类型构造函数Int 的部分应用。

【讨论】:

  • 啊啊啊啊啊。那是个愚蠢的错误。我很抱歉。谢谢你。那么本质上类型构造函数的域是所有可能类型的集合,那么呢?
  • 或多或少。我确信我不完全理解种类的一些微妙之处,但wiki.haskell.org/Kind 将普通类型定义为具有种类* 并将类型构造函数定义为具有种类P -> Q 其中PQ 是种类。 (那里的额外通用性允许(->) 有亲切的* -> * -> *,其中PQ 分别是**->*。)
  • @chepner 次要术语更正:“类型构造函数”是一个技术术语,与具有P -> Q 形式的属性正交。例如,Int* 类型的类型构造函数,Either Int 具有箭头类型但不是类型构造函数。类比术语构造函数:False 是没有箭头类型的构造函数,而 (+) 0 在其类型中有箭头但不是构造函数。
  • 我想我不明白为什么Either Int 不是类型构造函数,因为它接受一个参数并返回一个类型,但也许这不是该问题的地方。
  • @chepner,因为这不是“类型构造函数”的意思。
【解决方案2】:

您误解了来自 GHC 的错误消息,这也造成了您的困惑。这意味着“Num a => a -> a 是多态或限定类型,因此是非法的(在此上下文中)”。

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

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

【讨论】:

  • 谢谢。我开始明白了,我想。您是否碰巧知道我可以查看以更好地理解您在第二段中的解释的另一个来源?我真的很想好好处理这些东西。
【解决方案3】:

类型签名几乎是正确的

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    相关资源
    最近更新 更多