Haskell的类型设计
Haskell是一个强类型的语言,所有元素皆有类型。以下是例子:
Prelude> :t 1
1 :: Num t => t
Prelude> :t 'a'
'a' :: Char
Prelude>
Prelude> :t "Hello"
"Hello" :: [Char]
如上代码所示,我们使用:t
来显示元素的类型。比如数字1
是Num
类型,a
是Char
类型,Hello
是Char的数组,显示为[Char]
。
Haskell是函数式语言,函数是它的核心,我们通过以下方式定义函数:
Prelude> add x = x + x
如上所示,我们定义了一个add
函数,它接受一个参数x
,它的功能是x + x
,就是把输入翻倍。我们可以使用这个函数:
Prelude> add 2
4
如上所示,我们使用add
函数,参数为2
,结果输出为4
,也就是2+2
,和预期一致。
函数当然也有类型,我们同样可以使用:t
来查看它的类型:
Prelude> :t add
add :: Num a => a -> a
如上所示,add函数的类型是a -> a
,含义就是”输入为a,输出为a”,其中a
是一种类型,它的类型是Num a
。
为什么我们的add定义是add x = x + x
,而Haskell判断出add函数的输入输出类型是Num
?因为使用了+
加号,因为对参量做了相加,所以Haskell猜我们的参数是Num
类型。
我们再写个函数:
Prelude> con x = x ++ x
我们定义了一个con
函数,它接受一个x
参量,做的运算是++
,这个运算有很多类型的数据都支持,比如Haskell里的列表类型,字串(实际上Haskell没有String类型,字串上面看到了是Char数组)类型,都支持++
,作用是把两个列表或字串合成一个。
因此,Haskell猜不了x的类型,那么这个con的类型定义会是怎样的?以下是答案:
Prelude> :t con
con :: [a] -> [a]
我们可以看到,Haskell并没有把类型”a”具体化,它只是抽象的(多态)。但它要求输入和输出都是一种类型(定义为”a”)。我们可以用用看自己定义的这个con函数:
Prelude> con "CAFE"
"CAFECAFE"
Prelude> con [1, 2, 3]
[1,2,3,1,2,3]
如上所示,con函数和我们分析的一样,既可以用在CAFE
这种Char数组类型上,也可以用在列表类型上([1, 2, 3]
是Haskell的列表类型,后续细讲),因为这两种类型都支持++
操作符。
所以我们学到了一点:Haskell是强类型语言。
但是Haskell不强制我们定义类型,它会猜类型。但我们写函数的时候,可以明确类型:
Prelude> con :: [Char] -> [Char] ; con x = x ++ x
Prelude> :t con
con :: [Char] -> [Char]
Prelude> con "CAFE"
"CAFECAFE"
如上所示,我们明确定义了con的类型是[Char] -> [Char]
,即输入类型为Char数组,输出也一样是Char数组。然后我们使用:t
验证了这点,最后我们使用了con函数。
注意:在上面的con函数定义中,类型和函数本身定义在了一行,用;
分割:
con :: [Char] -> [Char] ; con x = x ++ x
这是因为Haskell的REPL,也就是GHCi,不支持多行定义。如果我们不写在一行,先写类型定义,会报错:
*Main> con :: [Char] -> [Char]
<interactive>:2:1: error:
• No instance for (Show ([Char] -> [Char]))
arising from a use of ‘print’
(maybe you haven't applied a function to enough arguments?)
• In a stmt of an interactive GHCi command: print it
但如果我们把代码保存在文件里面,命名为con.hs
:
$ cat con.hs
con :: [Char] -> [Char]
con x = x ++ x
然后在GHCi里面读入:
$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
Prelude> :l con
[1 of 1] Compiling Main ( con.hs, interpreted )
Ok, one module loaded.
这样就是没有问题的。
最后我们试试来使用con函数应用在不是Char数组类型的数据上:
Prelude> con [1, 2, 3]
<interactive>:26:6: error:
• No instance for (Num Char) arising from the literal ‘1’
• In the expression: 1
In the first argument of ‘con’, namely ‘[1, 2, 3]’
In the expression: con [1, 2, 3]
如上所示,我们看到,此时con不再接受这种类型,因为我们明确定义了con函数的类型:
*Main> :t con
con :: [Char] -> [Char]
Haskell的类型定义就讲到这里。