【问题标题】:What is the way to describe the type signature of Haskell functions that are not type-specific?描述非特定类型的 Haskell 函数的类型签名的方法是什么?
【发布时间】:2021-05-12 04:48:15
【问题描述】:

给定一个像negate 这样的函数,它具有类型签名:

negate :: Num a => a -> a

我将其描述为aNum 上下文中的类型(如果您认为我错了,请纠正我)。

但我不完全确定如何描述类似 last 的东西,它具有类型签名:

last :: [a] -> a

我的猜测是它不是特定类型的,它需要一个列表并产生与列表相同类型的单个值。这是正确的思考方式吗?

【问题讨论】:

  • Num a 是“约束”,Num 是“类型类”/“typeclass”,a -> a 是“参数多态类型”,a 是“类型变量”("parametrically-polymorphic" 中的“参数”)。 [b] -> b 是参数多态类型,[b] 是参数多态类型,b 是类型变量。

标签: function haskell types type-signature haskell-prelude


【解决方案1】:

last :: [a] -> aSystem F 类型的 Haskell98 语法

last :: ∀ a. [a] -> a

∀ a 可以理解为一种类型级别的 lambda 绑定,即在实际值级别列表参数之前,函数接受一个类型级别参数,告知列表中包含的元素的类型。这种通用量化使得函数参数化多态

通常,类型检查器会自动插入类型变量。在较新的 GHC Haskell 中,您还可以显式应用它们:

Prelude> :set -XTypeApplications 
Prelude> :t last @Int
last @Int :: [Int] -> Int
Prelude> last @Double [5,6,7]
7.0

negate 也是参数多态的,但与last 不同的是,它不能真正“适用于所有”类型,而仅适用于具有Num 实例的那些(IntDouble 都可以,但不是例如Char)。换句话说,它不仅接受一个额外的参数指定类型,还接受一个证明它确实有一个Num实例。它也会被编译器自动插入。

negate :: ∀ a. Num a => a -> a
Prelude> :t negate @Int
negate @Int :: Int -> Int
Prelude> :t negate @Char

<interactive>:1:1: error:
    No instance for (Num Char) arising from a use of ‘negate’

【讨论】:

    【解决方案2】:

    negate 中,您需要对传递的元素进行操作,在您的情况下,您可以对其应用一些运算符,例如(-): negate a = -a.

    您不能为 any 类型定义negate,因为您需要能够在其上调用(-)。您需要保证给定的参数将是某种支持此操作的类型。

    Num 是一个类型类,它为您提供了这一点 - 编译时保证支持 (-),以及其他功能,如 +*。您可以在docs 中阅读更多相关信息

    相比之下,last :: [a] -&gt; a(需要)对实际的a 值做任何事情。它只接受它们并返回最后一个。虽然negatea 值进行操作,但这里last列表 进行操作,但不对其值做任何事情,它只是传递它们。因此,它不需要关于它们的任何知识,因此类型不受约束。

    【讨论】:

    • 或者你可以定义a - b = a + (negate b)
    【解决方案3】:

    首先,a 不是Num 上下文中的 类型,而是具有Num 实例的一个 类型。

    Num a =&gt; a -&gt; a 是一个约束 多态类型,而[a] -&gt; a 是一个不受约束的多态类型,或者简称为多态类型。在不受约束的情况下,a 可以是任何类型;在受约束的情况下,它必须是服从给定约束的类型。

    【讨论】:

      猜你喜欢
      • 2010-10-27
      • 1970-01-01
      • 2013-01-27
      • 2011-06-13
      • 2011-01-11
      • 1970-01-01
      • 2017-06-28
      • 1970-01-01
      • 2022-08-19
      相关资源
      最近更新 更多