围绕 Haskell 类型系统的术语说明

Clarification of Terms around Haskell Type system

haskell 中的类型系统似乎非常重要,我想澄清一些围绕 haskell 类型系统的术语。

  1. 一些类型 classes

    • Functor
    • Applicative
    • Monad

    使用:info后发现Functor是class的类型,Applicative是class和=>的类型(推导?) FunctorMonad 派生 Applicative 类型 class.

    我读到 Maybe 是一个 Monad,这是否意味着 Maybe 也是 ApplicativeFunctor

  2. -> 运算符

    当我定义类型时

    data Maybe = Just a | Nothing
    

    并检查 :t Just 我得到 Just :: a -> Maybe a。如何阅读这个 -> 运算符?

    它让我对函数感到困惑,其中 a -> b 意味着它计算 ab(可能是 returns)——我倾向于认为 lhs 到 rhs关联但在定义类型时会发生变化?

  3. 术语类型以模棱两可的方式使用,类型,类型Class,类型构造函数,具体类型等...我想知道它们的确切含义

确实,“类型”这个词的用法有些含糊。

也许最实用的看待它的方法是 类型 只是一组 。例如,Bool 是包含值 TrueFalse 的有限集。
在数学上,set[= 的概念之间存在细微差别153=] 和 type,但对于程序员来说,它们并不重要。但是您通常应该认为集合是无限的,例如 Integer 包含任意大的数字。

定义类型最明显的方法是使用 data 声明,在最简单的情况下,它只列出所有值:

data Colour = Red | Green | Blue

我们有一个类型,作为一个集合,包含三个值。

具体类型基本上就是我们所说的明确我们的意思是上面的意思:对应于一组值的特定类型。 Bool 是具体类型,可以很容易理解为 data 定义,但 StringMaybe IntegerDouble -> IO String 也是具体类型,尽管它们不' 对应于任何单个 data 声明。

具体类型不能有的是类型变量,也不能它是一个不完全应用的类型构造函数。例如,Maybe 不是具体类型。

那么什么是类型构造器?它是值构造函数的类型级模拟。 Haskell 中的“构造函数”在数学上的意思是一个 injective function,即一个函数 f 如果给定 f(x) 你可以清楚地识别出什么是x。此外,假设任何不同的构造函数都具有不相交的范围,这意味着您还可以识别 f.

Just 是值构造函数的一个例子,但它使讨论变得复杂,因为它也有一个类型参数。让我们考虑一个简化版本:

data MaybeInt = JustI Int | NothingI

现在我们有

JustI :: Int -> MaybeInt

这就是 JustI 是一个 函数 的原因。就像任何具有相同签名的函数一样,它可以应用于正确类型的参数值,比如,你可以写 JustI 5.
这个函数是单射的意味着我可以定义一个变量,比如说,

quoxy :: MaybeInt
quoxy = JustI 9328

然后我可以 模式匹配 JustI 构造函数:

> case quoxy of { JustI n -> print n }
9328

这对于具有相同签名的通用函数是不可能的:

foo :: Int -> MaybeInt
foo i = JustI $ negate i


> case quoxy of { foo n -> print n }
<interactive>:5:17: error: Parse error in pattern: foo

请注意,构造函数可以是 nullary,在这种情况下,单射 属性 是无意义的,因为没有包含单射函数的数据/参数。 NothingTrue 是 nullary 构造函数的示例。

类型构造函数与值构造函数的思想相同:类型级函数 可以进行模式匹配。任何用 data 定义的类型名都是类型构造函数,例如 BoolColourMaybe 都是类型构造函数。 BoolColour 是无效的,但是 Maybe 是一元类型构造函数:它接受一个类型参数,只有结果才是具体类型。

因此,与值级函数不同,类型级函数在某种程度上是默认类型构造函数。还有一些不是构造函数的类型级函数,但它们需要 -XTypeFamilies.

一个类型class可以理解为一组类型,同理一个类型可以是被视为一组值。这不是很准确,说 class 是 一组类型构造函数 更接近于真实,但是再次思考数学细节也没有那么有用 - 最好看看示例。

type-as-set-of-values 和 class-as-set-of-types 之间有两个主要区别:

  • 如何定义“元素”:在编写 data 声明时,您需要立即描述允许的值。相比之下,class 被定义为“空”,然后 实例 稍后定义,可能在不同的模块中。
  • 元素如何使用data 类型基本上枚举了所有值,因此它们可以再次被 识别。 类 同时通常不关心识别类型,而是指定元素类型满足的 属性 。这些属性以 方法 或 class 的形式出现。例如,Num class 的实例是具有 属性 的类型,您可以将元素加在一起。

您可以说,Haskell 在值级别上是静态类型的(每种类型中的固定值集),但在类型级别上是鸭子类型的(classes 只需要有人某个地方实现了必要的方法)。

Num 示例的简化版本:

class Num a where
  (+) :: a -> a -> a

instance Num Int where
  0 + x = x
  x + y = ...

如果 + 运算符尚未在前奏中定义,您现在可以将其与 Int 数字一起使用。稍后,也许在不同的模块中,您还可以将其用于新的自定义数字类型:

data MyNumberType = BinDigits [Bool]

instance Num MyNumberType where
  BinDigits [] + BinDigits l = BinDigits l
  BinDigits (False:ds) + BinDigits (False:es)
    = BinDigits (False : ...)

Num 不同,Functor...Monad 类型 classes 不是 class es 的类型,但是 1-ary 类型构造函数。 IE。每个仿函数都是一个类型构造函数,它接受一个参数使其成为具体类型。例如,回想一下 Maybe 是 1 元类型构造函数。

class Functor f where
  fmap :: (a->b) -> f a -> f b

instance Functor Maybe where
  fmap f (Just a) = Just (f a)
  fmap _ Nothing = Nothing

正如您自己得出的结论,ApplicativeFunctorsubclassDC 的子class 基本上意味着 DC 中的一组类型构造函数。因此,是的,如果 MaybeMonad 的一个实例,它也是 Functor.

的一个实例
这不完全正确:如果您将_universal quantor_明确视为类型的一部分,那么具体类型可以包含变量。不过这有点高级。

如果使用了 -XPatternSynonyms 扩展名,则不能保证这一点。