强大的类型系统是Haskell的一个非常大的优势。
Haskell所有表达式类型在编译期判断。这样的话,可以使得代码
更加安全,比如说,拿一个整数和一个字符串进行除法运算是没办法进行的,那么在编译器就会直接报错,不会等到运行时程序崩溃才知道。Haskell与
Java不一样,Haskell能够进行类型推断(Type
Inference),也就是说,你不需要明确的说100是个数字,或者说是整型,编译期能推断出这是一个整型。
在GHCi中,我们可以使用:t 命令来检测一个表达式的类型。
Prelude> :t 'q'
'q' :: Char
Prelude> :t "aaa"
"aaa" :: [Char]
::操作符的含义是“具有…类型”。也就是说,根据上面的结果,我们
知道,字符q的类型是Char。一般来说,Haskell的类型的首字母都是大写,比如上面提到的Char,还有Bool或者Boolean。[]代表
List,[Char]代表元素类型为Char的List。()则代表 Tuple,('a','a')的类型是(Char,Char)。
1.显式类型声明
除了表达式之外,函数也是有类型的。我们在定义函数的时候,可以显式给函数声明其类型。我们在前面讲过一个去处字符串中大写字母的List Comprehension:
removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]
对于这样一个函数,很明显,其输入和输出都是字符串,也就是字符List,因此,我们可以这样声明函数的类型:
removeNonUppercase :: [Char]->[Char]
上面这个声明的含义是,函数removeNonUppercase接
收一个[Char]参数,并且返回一个[Char]。那怎么去指定一个接收多个参数的函数的类型呢?比如说有一个函数叫addThree,接收三个参数,
并且将这三个参数的值相加并且返回。我们可以这样指定addThree的函数类型:
addThree :: Int->Int->Int->Int
也就是说,最后一个会被当做返回值来解析,前面的都会被当做参数来解析。如果说你不知道你要写的函数到底应该是什么类型,你可以先把函数写出来,然后使用:t命令看看到底是什么类型,最后再补上函数类型指定。
2.常见的Haskell类型
类型
说明
Int
整型,但是能表示的整数有界限(达到一定程度就会溢出),效率更高
Integer
整型,能够表示的整数没有界限,效率低
Float
单精度浮点数
Double
双精度浮点数
Bool
布尔值,只有True和False两个值
Char
单个Unicode字符
Tuple
具体的Tuple类型取决于元素的类型和个数,理论上有无数Tuple类型,但是实际上Tuple最多只能有63个元素
3.类型变量(Type Variable)
有时候函数需要能够处理多种类型的数据,我们以head函数为例。首先看看head函数的类型:
Prelude> :t head
head :: [a] –> a
我们可以看到,函数head接收一个List作为输入,返回List
中的一个元素。但是这个元素到底是Char还是Int还是Bool并不重要。这个a是什么?我们说过所有的类型都是以大写字母打头的,a显然不是一种我们
所不知道的类型。a实际上就是我们这里说的类型变量的一个例子。类型变量能够允许函数以一种安全的方式操作多种类型,这一点类似于Java中的泛型。使用
类型变量的函数在Haskell中称为多态函数(Polymorphyc
function)。head函数的定义的含义是:head接收一个装任何元素的List,返回这种类型的一个值。
我们再看看fst函数的类型定义:
Prelude> :t fst
fst :: (a, b) –> a
这个函数接收一个pair,然后返回第一个元素,至于这个pair的元素可以是任何类型,这里的a,b都是类型变量。需要说明的是,这里的a和b虽然都是类型变量,但是不意味着他们一定是不同的类型。
4.Type Class
Type Class我也不知道该怎么翻译比较合适。Type
Class实际上是一种借口,它定义一些行为,当某个变量是这个Type Class的实例,那么它可以实现这个Type
Class所描述的行为。Type Class一般指定一组函数,一个变量是该Type
Class的实例,我们就需要确定这些函数对于这个变量本身有什么意义(也就是说这个变量要有自己的实现)。
定义相等性的Type Class就是一个很好的例子。很多类型都可以用==来看值是否相等。我们先看看==运算符的函数签名:
Prelude> :t (==)
(==) :: Eq a => a -> a –> Bool
实际上==是一个函数,基本上+,-,*以及几乎所有的运算符都是函
数。这里出现了一个新的符号=>,所有出现在这个符号之前的部分叫做class
constraint。这个函数类型的意思是:==函数接收两个值,他们同样属于类型Eq,函数最终返回一个Bool值。
Eq就属于Type
Class,它提供了判断值是否相等的接口。而这些值必须是相同类型才有比较的意义,这些值可以使Eq的实例。事实上,在标准的Haskell中,几乎所
有类型都是Eq的实例。需要特别指出的是,Type
Class并不是面向对象编程语言中的Class。下面我们一起看看Haskell中常见的集中Type Class:
Eq用来提供检测值是否相等的接口。它的两个实现是==和/=。这意
味着如果在一个函数的定义中出现了Eq class
constraint,那么这个函数的定义中肯定用到了==或者是/=。如果一种类型实现一个函数,他就要定义使用这个类型的值时,该函数到底做些什么。
我们看几个Eq实例进行相等性比较时的例子:
Prelude> 5 == 5
True
Prelude> 'q' == 'q'
True
Prelude> "Hello"=="hello"
False
Prelude> "Hello"=="Hello"
True
Prelude> pi == 3.14
False
我们可以看到,字符串的比较规则是遵循List的相等性比较,与Java中的比较引用是不一样的。
Ord是一种为那些可以将值放在某种顺序排列中的类型设计的Type Class。我们看看>函数的类型:
Prelude> :t (>)
(>) :: Ord a => a -> a –> Bool
>与==比较类似,都接收两个参数,然后返回一个Bool值。Ord Type Class涉及到了所有的比较函数:> < >= <=。
compare函数接收两个参数,这两个参数的类型都是Ord的实例,然后返回一个Ordering。Ordering是一个值可以是GT、LT或者EQ的类型,分别代表大于、小于和等于。我们看几个例子:
Prelude> "abcd" `compare` "bbcd"
LT
Prelude> "abcd" `compare` "abbd"
GT
Prelude> "abcd" `compare` "abcd"
EQ
类型是Show这个Type Class的实例的值可以被显示为字符串。对于所有属于Show这个Type Class的实例的类型来说,使用最多的函数式show(s小写)。我们看几个例子:
Prelude> show 3
"3"
Prelude> show True
"True"
Read可以看做是Show的反面。read函数接收一个字符串,然后返回一个类型是Read的实例的值。看例子:
Prelude> read "True" || False
True
Prelude> read "5"-2
3
Prelude> read "[1,2,3,4]" ++ [5]
[1,2,3,4,5]
目前为止都一切正常,我们再看一个例子:
Prelude> read "5"
<interactive>:30:1:
Ambiguous type variable `a0' in the constraint:
(Read a0) arising from a use of `read'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: read "5"
In an equation for `it': it = read "5"
当我们直接read "5"时,GHCi不知道该返回什么。我们之前的例子都将read返回的结果再参与某种运算,这样GHCi才好进行类型推断,这就是为什么read "5"没办法返回值的原因。我们看一下read函数的原型:
Prelude> :t read
read :: Read a => String –> a
我们看到,read函数接收String,但是返回一个类型是Read的实例的值。但是类型是Read实例的类型太多了,GHCi不知道到底选哪一种类型。这种情况下,我们可以使用类型注解(type annotation)。我们看例子是最直接的:
Prelude> read "5" :: Int
5
Prelude> read "5" :: Float
5.0
对于read来说还需要举一个例子:
Prelude> [read "True",False,True,False]
[True,False,True,False]
因为List中的每一个元素必须属于种类型,所以read "True"的返回值必须和其他元素类型一样,也就是Bool,这样,GHCi就知道该怎么返回值了。
Enum的实例是那种值有序的类型——他们的值可以被枚举。Enum
Type
Class最大的优势是可以在Ranges中使用其值。他们还定义了successors设predecessors,我们可以分别通过succ和
pred两个函数获得。Bool、Char、Ordering、Int、Integer、Float、Double是这个Type
Class的实例,我们看例子:
Prelude> ['a'..'e']
"abcde"
Prelude> [LT .. GT]
[LT,EQ,GT]
Prelude> [3 .. 5]
[3,4,5]
Prelude> succ 'B'
'C'
Prelude> pred 'B'
'A'
那些是Bounded实例的类型有一个上限值和一个下限值。分别可以使用minBound和maxBound查看:
Prelude> minBound::Int
-2147483648
Prelude> maxBound::Int
2147483647
minBound和maxBound的类型都是Bounded a=>a。准确来说,他们是多态常量。Tuple中所有元素类型都是Bounded的话,那么这个Tuple也被认为是Bounded的实例。
Num是数字Type Class,它的实例都是数字。所有的数字都是多态常量。也就是说我们可以将它制定成Num下属类型中的任何一种:
Prelude> 6::Int
6
Prelude> 6::Float
6.0
要成为Num Type Class的实例,这个类型必须要已经是Eq和Show Type Class的实例。
顾名思义,这种Type Class的实例类型就是用来存储浮点数的,就两种类型Float和Double。
包括Int和Integer两种。介绍两个函数fromIntegral和length,先看看两个函数的签名,再看看怎么使用:
Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
Prelude> :t length
length :: [a] –> Int
Prelude> fromIntegral (length [1,2,3,4]) + 3.4
7.4
5.Tips
Type Class实际上是一个抽象的接口,所以一个类型可以是多种Type Class的实例,同样,一种Type Class有很多实例;
有时候一种类型必须先是一种Type Class的实例才会被允许成为另一个Type Class的实例。
分享到:
相关推荐
4. 第四部分可能包括IO操作,展示如何在纯函数式环境中进行输入输出。 5. 还可能有高级主题,如类型家族、GADTs(通用关联类型)和Quasiquoters等。 6. 最后,教程通常会提供一些练习和项目,让读者有机会实践所学的...
By working through 43 easy-to-follow lessons, you'll learn Haskell the best possible way—by doing Haskell! Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from ...
Haskell 学习资料,Learn you a Haskell.
### 关于《Learn You A Haskell For Great Good》的知识点总结 #### 一、书籍基本信息概述 本书名为《Learn You A Haskell For Great Good》,是一本专为Haskell编程语言初学者编写的指南。作者是Miran Lipovaca,...
http://learnyouahaskell.com/ 这个网站的pdf版,比较系统的haskell教程,不过是英文的
为了学习Haskell,你可以参考《Haskell编程从入门到实践》、《Real World Haskell》等书籍,以及在线教程如"Learn You a Haskell for Great Good!"。此外,Stack Overflow、Haskell Cafe和Haskell subreddit等社区...
《学习您的Haskell笔记本》是基于Jupyter Notebook的Haskell学习资源,改编自经典的Haskell教程《Learn You a Haskell for Great Good!》。这个项目旨在为Haskell初学者提供一个交互式的学习环境,通过Jupyter ...
在本文中,我们将深入探讨Haskell这一纯函数式编程语言,它是编程..."learn-haskell-master"这个项目可能是Haskell初学者的学习资源,包含教程、示例代码和练习,对于想要深入理解Haskell的人来说是一个很好的起点。
《基于第一原理的Haskell》是一本深入探讨Haskell编程语言的著作,旨在通过解析Haskell的核心概念,帮助读者从基础出发理解这门函数式编程语言。Haskell以其纯净的函数式特性、静态类型系统和强类型推导而闻名,是...