热爱函数式的你,句句纯正的 Haskell【类型篇】

1,910 阅读7分钟

每次看到干尸鬼鲛起舞,都有一种说不出的难受,不行,发出来,让大家一起难受难受~🐶

Haskell 是一门纯的函数式语言。 也就是说计算机主要是通过函数来完成的(像在数学中一样),而不是通过“先做这个,再做那个”的命令式操作顺序进行的(像在主流的编程语言中一样)。—— Simon Peyton Jones

初见😀

什么是 Haskell ?我们从 wiki 上可以找到以下要点:

  • Haskell 是一种标准化的,通用的纯函数式编程语言,有惰性求值和强静态类型;
  • 在Haskell中,“函数是第一类对象”。作为一门函数编程语言,主要控制结构是函数
  • Haskell具有 “证明即程序、命题为类型” 的特征;

这些概念起初可能看起来空泛,但回过头来看:“它还真就是这样!”

调试🎇

目前 Haskell 的主要编译器是 GHC,下载地址,你可以创建 .hs 文件,用 Notepad++ 打开。

GHCi 是 GHC 的一部分,可以解析、调试 Haskell 程序。

image.png

认识下 GHCi 中的命令:

  • :l 用来导入当前路径或者指定路径下的文件;
Prelude> :l "C:\\Users\\ User\\Desktop\\HelloWorld\\HelloWorld.hs"
  • :r 用来重新导入当前的源代码文件;

  • :cd 改变当前GHCi的路径;

Prelude> :cd C:\Users\User\Desktop
  • :edit 用默认的文本编辑器编辑当前导入的文件;

更多命令可见:官网

HelloWorld🌎

对于每个程序员来说,Hello,World 都是神圣的!

可以直接在调试器内打印:

putStrLn "Hello,World!"

也可以新建一个文件:Helloworld.hs

然后 :l 引入,输入 main 运行:

Prelude> :l D://ghc-haskell//test.hs

*Main> main
Hello,World!

也可以通过 :cd 命令,输入 runghc ,将 .hs 文件变成 .exe 文件执行;

类型🍕

Haskell 的类型属于强类型,即每一个数据或每一个函数都有非常精确、严格的类型。

注:我们使用命令 :t 来查看类型;

Haskell 常用数据类型有:

  • Bool

布尔类型只有 True 和 False 两个值,注意大小写;同样支持“或与非”运算:

True||False

True&&False

not True
  • Char

字符型,与其它语言一致

Prelude> :t "str"
"str" :: [Char]
  • Int

有符号整数,它的范围与操作系统和 GHC 位数有关。

  • Word

无符号整数,Haskell 中的 Word 相当于 C 语言里的 unsigned int 类型;

  • Integer

任意精度整数;

  • Float

单精度浮点数;

  • Double

双精度浮点数;

  • Rational

有理数类型 Rational,即用两个任意精度的整数来表示一个小数,这在做高精度数学运算时有很多好处;

Prelude> 0.75::Rational
3 % 4
  • String

字符串类型,String 是一个 Char 的列表。

在 GHCi 里输入['H', 'e', 'l', 'l','o'],会得到 "Hello"

Prelude> ['H', 'e', 'l', 'l','o']
"Hello"
  • tuple

元组类型,如:(7758,True,"HelloWorld"),各种类型可以互相组合使用;

以上,都是基础的类型,可一眼带过~

函数类型!🍻

函数类型是本篇的重中之重,前面的可以随意看看,但是从此节开始请务必细究。

函数可以理解为从参数到结果的一个映射,比如T1 -> T2。每一个函数都符合这样一个定义;

add::(Int,Int)->Int // 声明 add 函数,输入是一个元组类型,元组内是两个 Int 元件,输出是一个 Int 类型;

add (x,y) = x + y // add 的具体实现

也可以这样直接在命令行中定义:

Prelude> let add(x,y) = (x + y) ::Int
Prelude> add(1,2)
3

若 T1 或 T2 为函数,那么 T1-> T2 函数可以称为高阶函数;这也是之前说过的,将函数作为输入或输出的函数称为高级函数

  • Haskell 柯里化

显然,两数相加传 2 个 Int 的元组,三个数相加传 3 个 Int 的元组,四个数相加,传 4 个 Int 的元组...... 这是非柯里化的,传参有极大麻烦;

add3::(Int,Int,Int)->Int

add4::(Int,Int,Int,Int)->Int

Haskell 定义了柯里化(curry)函数来帮助我们改善这一点:

Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c

// 实现加法的柯里化,支持多项连续相加,且不用提前声明项数;

Prelude> let add(x,y) = (x + y) ::Int
Prelude> curry add(curry add((curry add)1 2)3)4
10

这个是真滴强👍

image.png

  • 多态函数

多态函数在 Haskell 中非常常见:

Prelude> head[1,2,3]
1

Prelude> head[True,False]
True

Prelude> head"Hello"
'H'

Prelude> zip[1,2,3][4,5,6]
[(1,4),(2,5),(3,6)]

[('a',1),('b',2),('c',3)]

Prelude> fst(5,True)
5

Prelude> snd(5,True)
True
  • 重载类型函数

5 一直是被当成整数。但是,它还可以是一个任意精度整数,或是一个小数。这样一来,类型上可能会有一些不协调,因为 5 是一个有着很多类型的值,Haskell 中用类型类(typeclass)这一概念来对这些类型做了细致的分类。

我们在下一小节做更为细致的说明“类型类”~

  • 类型别名

一个数据的类型可以由多个其他的类型组成,在 Haskell 中,可以用 type 关键字将这些复杂的类型替换成为其他简单的名字;

Prelude> type RGB=(Int,Int,Int)
Prelude> let rgb=(255,255,255) ::RGB
Prelude> :t rgb
rgb :: RGB

这样处理后,你能更清楚这个变量是干什么的~

类型类!!🖖

在控制台输入 :t 5 查看输出:

Prelude> :t 5
5 :: Num p => p

5 是 Num 类型类,这个数可以是整数,也可以是小数或其他数类型;

  • => 是类型类的限定符号;

Haskell 除了 Num 类型类以外,还有Eq、Ord 和 Show 类型类等等;

// 判断是否相等 Eq 类型类
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool

// 判断大小 Ord 类型类
Prelude> :t (<)
(<) :: Ord a => a -> a -> Bool

// 使用 Show 打印 <
Prelude> :m + Text.Show.Functions
Prelude Text.Show.Functions> show(<)
"<function>"

// 枚举类型类
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]

......

image.png

上图不在灰色方框内的部分全部是类型类;

Haskell 给很多“类型”分成了“类型类”,归为一类的类型有着共同的属性,不同类型所归的类就称为类型类。

每个类型类下面都写了一些该类型类中预定义的函数,我们可以接着打印输出体验:

// fromInteger 是 Num 类型类下的函数,可以将一个一个的整数转为一个重载的数类型 a
Prelude> :t fromInteger
fromInteger :: Num a => Integer -> a

有时需要将一个整数转为复数类型或者比值类型,这时就可以使用它。

Prelude> :m Data.Ratio
Prelude Data.Ratio> fromInteger 5 :: Ratio Int
5 % 1

还有熟悉的向下取整方法:

Prelude> :t floor
floor :: (RealFrac a, Integral b) => a -> b

Prelude> floor(1.2)
1

类型类中定义了一些函数,如果定义了一个新的类型,只要这个类型实现了类型类中声明的函数这个类型就属于该类型类了;

小结🤖

入门第一篇,类型在程序语言中非常重要!

强类型:可以帮助我们检查错误、对程序进行抽象(函数式编程关键)、具有文档说明作用。

可以看出,Haskell 的严格定义类型和 javaScript 中还是有较大差异,一个强类型,一个弱类型~

强类型适合大型项目的维护,弱类型与动态性结合,开发简单,处理灵活;

Haskell 的类型类,以及类型类底下的各种函数,真的太好用了吧~ 不用理会类型转换,特别是像 js 中的隐式转换,真的太爽了~

在逐渐学习的过程中,不断提升强类型设计精髓的理解。

看到这里,点个赞吧~ 👍 掘文不易,还需鼓励~ 👏

我是掘金安东尼,输出暴露输入,技术洞见生活,再会~