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

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


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 中的隐式转换,真的太爽了~


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

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

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


相关文章
|
前端开发 JavaScript 索引
三大应用场景调研,Webpack 新功能 Module Federation 深入解析
Federated Modules 是一个令人激动的功能,它可能会改变未来几年的前端打包方式,作者深入分析了 Module Federation 的原理及其应用场景,希望能对大家有所启发。
12801 0
三大应用场景调研,Webpack 新功能 Module Federation 深入解析
|
安全 机器人 测试技术
带你读《无人机网络与通信》之一:无人机系统概述
本书针对无人机系统两个关键问题—通信组网和管控体系做了比较全面和深入的描述和探讨,特别是以大量笔墨分析了现有无线通信解决方案,对比了不同通信协议,得出了很有价值的研究结论。无人机的跨越式发展将涉及公共安全管理的问题,构建管控体系是当务之急,分级管理以及制定相应的适航标准是一件大事情,本书对此也进行了系统的、建设性的讨论。未来,高档无人机和无人机集群将对环境具有更强的感知能力和自适应能力,还有对任务的自规划和学习、调整能力,本书讨论的内容将为它们的发明、部署和监督提供宝贵的信息。
|
Web App开发 定位技术
|
图形学 Android开发
Unity粒子特效系列-下雨预制体做好了,拿走直接就用!
众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!
2271 1
|
SQL 算法 Java
百万级别数据Excel导出优化
这篇文章不是标题党,下文会通过一个仿真例子分析如何优化百万级别数据Excel导出。
1595 0
百万级别数据Excel导出优化
|
存储 SQL 消息中间件
基于MaxCompute+Hologres的人群圈选和数据服务实践
基于MaxCompute+Hologres的人群圈选和数据服务实践
2080 1
基于MaxCompute+Hologres的人群圈选和数据服务实践
|
机器学习/深度学习 算法 Python
机器学习常见异常和解决办法汇总
scikit-learn是一个Python机器学习库,提供了很多基础的模型和算法。
|
数据采集 SQL 存储
DataWorks 数据分析介绍及实践 | 学习笔记
快速学习 DataWorks 数据分析介绍及实践,介绍了 DataWorks 数据分析介绍及实践系统机制, 以及在实际应用过程中如何使用。
DataWorks 数据分析介绍及实践 | 学习笔记
|
机器学习/深度学习 人工智能 算法
VGGNet架构解析
VGGNet 是 2014 年 ImageNet Challenge 图像识别比赛的亚军。参赛团队是来自牛津 大学的研究组 VGG (Visual Geometry Group) 。VGGNet 的很多设计思想都受到 AlexNet 的影响,所以跟 AlexNet 也有一点点相似的地方。VGGNet 不仅在图像识别方向有着广泛应 用,很多目标检测,目标分割,人脸识别等方面的应用也会使用 VGGNet 作为基础模型。
821 0
VGGNet架构解析
|
存储 前端开发 JavaScript
【长文】微前端很好,为什么我却不使用? | 微前端原理剖析
本文记录我对微前端原理的探索与思考,以及微前端框架qiankun项目实践。

热门文章

最新文章