Haskell学习——类型

本文介绍了Haskell语言的基本类型如Char、Bool、Int等,并详细解释了列表和元组的概念及操作方法。此外,文中还探讨了Haskell的类型签名和类型推导机制。


Haskell一大特色就是它的强类型特性和静态类型检查,可以在编译阶段知道所有表达式的类型,并发现大部分潜在错误。

关于类型系统更细节更抽象的部分,可以参考Types and Programming Language一书。

本篇主要参考RWH,英语真的是硬伤啊orz


基本类型

Char
单个 Unicode 字符。
Bool
表示一个布尔逻辑值。这个类型只有两个值: True 和 False 。

Int
符号的定长(fixed-width)整数。这个值的准确范围由机器决定:在 32 位机器里, Int 为 32 位宽,在 64 位机器里, Int 为 64 位宽。Haskell 保证 Int 的宽度不少于 28 位。(数值类型还可以是 8 位、16 位,等等,也可以是带符号和无符号的,以后会介绍。)

Integer
不限长度的带符号整数。 Integer 并不像 Int 那么常用,因为它们需要更多的内存和更大的计算量。另一方面,对 Integer 的计算不会造成溢出,因此使用 Integer 的计算结果更可靠。
Double
用于表示浮点数。长度由机器决定,通常是 64 位。(Haskell 也有 Float 类型,但是并不推荐使用,因为编译器都是针对 Double 来进行优化的,而 Float 类型值的计算要慢得多。)

:: 符号除了用来表示类型之外,它还可以用于进行类型签名。比如说, exp :: T 就是向 Haskell 表示, exp 的类型是 T ,而 :: T 就是表达式 exp 的类型签名。如果一个表达式没有显式地指名类型的话,那么它的类型就通过自动推导来决定。

当然,若类型签名和类型推导的结果不同,它是会报错的。


关于这些普通类型的运算符,Haskell显然是受到c的强烈影响,比如True && False, 20 * 3什么的,几乎完全一样。

不过也有一些不同。比如逻辑非,Haskell采用的是not;不等于,Haskell里用的是 /= 而不是 !=,妥妥的模仿≠嘛。

关于这些运算符的优先级,查看info就好,infixr / infixl 越大表示优先级越高,其中的 r / l 表示该运算符是右结合的还是左结合的。


列表

[1,2,3],['a','b','c']什么的,元素类型必须相同。

比较有意思的是列举符号,比如[1..5]就表示[1,2,3,4,5],[1,3..6]就表示[1,3,5]什么的。它只可以用于那些可以被枚举的类型。

可以在ghci里试一下[1..],呵呵。

列举浮点数的时候可能会有些困惑。比如[1.0..1.8]会表示[1.0,2.0],这是由于没有规定步长,因此采用了先舍入再枚举的原则。

连接两个列表的操作符是++,比如你可以这么操作:

ghci> [3,1,3] ++ [3,7]
[3,1,3,3,7]
ghci> [] ++ [False,True] ++ [True]
[False,True,True]
还有一种在函数式语言里非常流行的一种读作cons的运算符,它负责将单独一个元素添加到列表的最前面。
ghci> 1 : [2,3]
[1,2,3]
ghci> 1 : []
[1]

说到列表就不得不说字符串。因为Haskell中的字符串完全就是字符列表的语法糖:

ghci> let a = ['l', 'o', 't', 's', ' ', 'o', 'f', ' ', 'w', 'o', 'r', 'k']
ghci> a
"lots of work"
ghci> a == "lots of work”
True
ghci> "" == []
True
ghci> 'a':"bc"
"abc"
ghci> "foo" ++ "bar"
"foobar"

在我测试空列表的类型时,出现了以下的结果:

<pre name="code" class="plain">Prelude> :type []
[] :: [a]
Prelude> :type [[]]
[[]] :: [[a]]
Prelude> :type [[],[]]
[[],[]] :: [[a]]
Prelude> :type [[],[1]]
[[],[1]] :: Num a => [[a]]
 这是函数式语言中的polymorphic,a仅仅是一个占位符,在类型检查的时候可能会被替换成更加具体的类型。 

而字符串虽然本质是列表,但只能容纳Char类型,即使是空串:

Prelude> :type ""
"" :: [Char]

像其他的函数式语言一样,Haskell为列表提供了head和tail函数:

Prelude> head [1, 2, 3, 4]
1
Prelude> head ['a', 'b', 'c']
'a'
Prelude> head []
*** Exception: Prelude.head: empty list
Prelude> tail [1, 2, 3, 4]
[2,3,4]
Prelude> tail [2, 3, 4]
[3,4]
Prelude> tail [True, False]
[False]
Prelude> tail "list"
"ist"
Prelude> tail []
*** Exception: Prelude.tail: empty list

另外还有一些其他的关于列表元素操作的函数,在此只举出两例:

Prelude> take 2 [1, 2, 3, 4, 5]
[1,2]
Prelude> drop 2 [1, 2, 3, 4, 5]
[3,4,5]


元组

元组和列表很像,它是以圆括号来表示的:

Prelude> (1964, "Labyrinths")
(1964,"Labyrinths”)
Prelude> :type (True, "hello")
(True, "hello") :: (Bool, [Char])
Prelude> (4, ['a', 'm'], (16, True))
(4,"am",(16,True))

关于列表和元组的区别,RWH中有一句比较经典的话:

A tuple is a fixed-size collection of values, where each value can have a different type.
This distinguishes them from a list, which can have any length, but whose elements must
all have the same type.
另外由于元组定长的特性,类型 () 的值是唯一的——只有 () ,而 (t) 会被当作 t 本身。这也比较符合人类的正常思维——我可不希望(1+1) * 2被当作一个元组和一个Num相乘。
Prelude> :type ()
() :: ()
Prelude> :type (1)
(1) :: Num a => a
Prelude> :type (1,'a')
(1,'a') :: Num t => (t, Char)
要注意,当且仅当两个元组对应位置的元素种类完全相同时它们的类型才相同。

元组是定长的,所以基本的操作函数少得多。对只有两个元素的元组,有:

Prelude> fst (1, 'a')
1
Prelude> snd (1, 'a')
'a'
同样的,在这里又看到polymorphic:
Prelude> :info fst
fst :: (a, b) -> a 	-- Defined in `Data.Tuple'
Prelude> :info snd
snd :: (a, b) -> b 	-- Defined in `Data.Tuple'
 

其实元组给函数同时返回多个值提供了方便。

题外话,ghc中对元组的实现可能是预定义的类型,比如下面的代码就无法运行:

Prelude> (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)

<interactive>:4:1:
    A 63-tuple is too large for GHC
      (max size is 62)
      Workaround: use nested tuples or define a data type
既然有max size的限制,我想我有充分的理由怀疑ghc是采取直接预定义了62个不同的父类型——由于元组的定长的特性,这样做是不会有任何问题的。

官方文档中这么讲:

Tuples are currently limited to size 100. HOWEVER: standard instances for tuples (Eq, Ord, Bounded, Ix Read, and Show) are available only up to 16-tuples.
This limitation is easily subvertible, so please ask if you get stuck on it.
额,其实我不知道为什么我的是62个。。小白表示底层太深奥。。


大概就这些?半翻译半实验真挺麻烦orz。。

话说Real World Haskell真是讲得好详细,我已经在怀疑写博文的必要性了。。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值