Haskell Lesson:类型系统解读
Posted 牛顿一号
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Haskell Lesson:类型系统解读相关的知识,希望对你有一定的参考价值。
每一种编程语言都有自己的一套类型系统,编译时又分为静态类型和动态类型,静态类型典型的如C语言,需要通过程序员自己指定类型并且匹配成功才能编译成功;动态的如python,不需要显示指定数据类型。直接从过程类语言过渡到Haskell的话,Haskell的类型系统可以说是第一个让人需要费点心思的地方了。
首先回忆一下C语言是怎么构造新类型的。
struct Test {
int index;
#define NAME_LEN 32
char name[NAME_LEN];
void (*get_name)(struct Test *);
};
这就是典型的C语言构造新类型的方法,即所谓的结构体。诸如此类的还有枚举类型、联合体。结构体定义了一个类型名字,成员都有其名字,并且根据名字访问到各个成员。
一、代数数据类型(algebraic data type)与类型构造子(data constructor)
定义类型: data TypeName = TypeConstructor [param] [| TypeConstructor [param]]
其中param可有可无,但是要注意如果param存在,那么其肯定是一种类型。比如data Test = Test Int
,表示传给一个Int类型的参数给类型构造子Test,这时候在ghci中执行:t Test
可以看到
*Main> :t Test
Test :: Int -> Test
还要注意第一个Test和第二个Test没有任何联系,第一个Test只是表示该类型名字叫Test,在代码中实际使用的是第二个Test(也就是类型构造子)。
二、类型解构与模式匹配
在前面实现Graham扫描算法的例子中,我们使用了很多的函数,基本上都是通过模式匹配实现的。再举个例子,譬如求和函数:
sum (x:xs) = x + sum xs
sum [] = 0
在第一次看到行如(x:xs)的时候,可能会有疑问--为什么有个括号?其实(x:xs)中的冒号(:)是一个类型构造子。在ghci中输入:t (:)
可以看到
*Main> :t (:)
(:) :: a -> [a] -> [a]
(:)就是用来构造列表的,而形如[1,2,3]的构造方式其实等价于(1:(2:(3:[])))。
说到这里,其实基本上就清楚了,模式匹配其实就是在对类型进行解构,使用的方法就是通过类型构造子的规定按照顺序进行参数匹配。
现在对前面定义的Test类型进行解构,在ghci中输入如下:
*Main> data Test=Test Int
*Main> getTest (Test a)=a
*Main> getTest (Test 10)
10
三、类型匹配
通过模式匹配得到类型之后,haskell还会进一步检查类型是否匹配。
关于类型匹配,这里可以先简单实验一下,在ghci中输入如下:
*Main> (\x->x+1)1
2
*Main>
*Main>
*Main> (\x->x+1)[]
<interactive>:37:1: error:
• Non type-variable argument in the constraint: Num [a]
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a. Num [a] => [a]
*Main>
第一次输入一个lambda表达式紧跟一个数1,这时候ghci给出了我们想要的结果2。当第二次我们入[]时,报的错误有点意思。
类型错误,这里有两点信息:
lambda表达式是匿名函数,所以函数参数的匹配似乎和函数名字没有直接关系。
错误的直接原因显示为类型不匹配,其实haskell自己已经通过类型系统推导出了当前输入x的类型是Num [a], 而实际上运算+期待的参数是Num a,所以产生类型错误。
类型匹配就是由haskell的类型推导系统完成的。
四、newtype/type/data
前面都是关于自定义类型data type的分析,除了data关键字以外,haskell还提供newtype和type关键字。type只是给已有的类型取一个别名,类型上并不会改变,编译器认为两者其实是一个类型。而newtype则不同,他会新定义一个类型,但是又有所限制。newtype只能有一个值构造器,而且构造器恰好只有一个字段,只存在于编译阶段。
五、代数数据类型实例
// algebraic data type without params
data Node = Node Int Char deriving (Show)
// algebraic data type with params
data Node a = Node a deriving (Show)
// record syntax, funcname :: data type
data Customer = Customer {
customerID :: CustomerID
, customerName :: String
, customerAddress :: Address
} deriving (Show)
// recursion data type definition
data Tree = Node Int Char (Tree) (Tree)
| Empty deriving (Show)
以上是关于Haskell Lesson:类型系统解读的主要内容,如果未能解决你的问题,请参考以下文章
Haskell 的类型系统是不是同构于一个不一致的逻辑系统?如果是这样,后果是啥?