方法签名Haskell用户定义的类型
我正在学习Haskell,并最终读到了这个,我试图找出代码的某些部分的作用。
首先,有一个数据类型定义:
infixl 4 :+:
infixl 5 :*:, :/:
infixr 6 :^:
data Expr a = Var Char
| Const a
| (Expr a) :+: (Expr a)
| (Expr a) :*: (Expr a)
| (Expr a) :^: (Expr a)
| (Expr a) :/: (Expr a)
deriving (Show, Eq)
接下来有一个功能:
simplify :: (Num a, Eq a, Floating a) => Expr a -> Expr a
simplify (Const a :+: Const b) = Const (a + b)
simplify (a :+: Const 0) = simplify a
simplify (Const 0 :+: a ) = simplify a
那么,在这种情况下,“(数字a,公式a,浮动a)=> Expr a”代表什么?
“Expr”不是构造函数,在第一行中,类型是“+(Expr Expr)”,那么“a”在这里意味着什么,为什么它限制为双精度? 在函数第二行中与“a”相关的类型签名是“a”,即使这里显然是Expr?
在数据定义中
data Expr a = ...
的a
装置,有一个类型变量那里。 一个简单的例子是
data Two a = Two a a
这是一个名为Two
的类型,它采用单个类型参数。 它有一个数据构造函数,也叫做Two
,它有两个字段,每个类型都是a
。 例如, Two Int
类型具有专门的构造函数Two :: Int -> Int -> Two Int
在这种情况下,你说你的Expr
类型有一个类型参数,并且有许多构造函数,其中一些具有类型为a
字段。 您可以使用它来获取Expr Int
类型的值,例如Const 1 :+: Const 2
。
当你看到类型签名像
simplify :: (Num a, Eq a, Floating a) => Expr a -> Expr a
这意味着该函数接受Expr a
类型的值并返回相同类型的值,除此之外,类型变量a
具有其他约束。 即Num
, Eq
和Floating
。 这些是类型类,并且类型a
必须实现这些类型类才能在Expr a
上使用simplify
。
在a
在类型签名无关与a
中定义的第一行simplify
。 这只是作者选择使用相同名称的选择。 类型名称和数据名称在Haskell中完全不同的名称空间中,它们根本没有冲突或关联。
对于每种类型的a
具有用于类型类实例Num
, Eq
和Floating
, simplify
可以专注于Expr a -> Expr a
。 类型和值的命名空间是分开的。 Expr
是一个类型构造函数。 Expr a
是一种Expr a
类型。 这里没有任何东西被限制为Double
, (+)
承认一切都是Num
实例。