Haskell 中的 Monad 和 IO

对于 Haskell 初学者来说,Monad 和 IO 或许是掌握 Haskell 之路上的第一大难关。本文将会以尽量浅显的方式介绍 Monad 和 IO 背后的原理和设计思想,希望能够给 Haskell 初学者们一些思考与启发。

本文假设您对函数式编程有一定的理解,因为这是讨论 Monad 和 IO 的理论基础。同时,本文会使用到一些 Haskell 基础语法,比如函数类型定义等。

本文需要您对偏函数和柯里化(currying)有所了解。如果您对此不了解,您可以参考网络上的其它文章。为了减轻读者的理解负担,本文仅在必要时采用柯里化的函数定义形式。

前言

我们知道,纯函数式编程中的函数必须是无副作用的。也就是说,这些函数 不能拥有状态,也 不能改变外界的状态(比如使用全局变量、在屏幕上输出信息),并且每一个输入必须 唯一对应一个输出

但是,现实中的程序几乎都需要进行一些有副作用的操作。比如,一个程序至少应该能够输出一些信息,或者写入一些文件等,否则这个程序将会毫无意义。因此,即使是函数式编程语言,也有必要引入有副作用的操作。

在 Haskell 中,与副作用有关的两个最重要的概念是 Monad 和 IO。本文将由浅入深地介绍这两个概念。

本文部分译自 Noel Winstanley 的文章 What the hell are Monads?。如果您对本文内容有不同见解,欢迎您对本文提出改进意见。

状态的引入

既然函数必须是无状态的,那么我们不妨先设法引入状态,作为讨论 IO 的一个开始。

假设我们用 Haskell 编写了一个数据库系统,并且提供一个 update 函数用于更新数据库内的记录。update 函数的定义如下:

update :: (DB, Int) -> Bool

update 函数向数据库中写入一个 Int 值,并返回一个 Bool 值表示操作是否成功。

但这样的定义是存在问题的,因为 update 函数必然会改变 DB 的状态,而根据函数式编程的基本规则,参数 DB 是不可变的,update 函数无法修改 DB 的状态。

解决方法也很简单,既然 update 会改变 DB 的状态,那么我们不妨让 update 函数除了返回一个 Bool 值外,还返回一个 更新了状态之后的 DB,如下:

update :: (DB, Int) -> (DB, Bool)

经过这样的修改,我们实际上就已经能够让 DB 对象拥有 内部状态 了。(外部状态 稍微复杂一些,这个问题将会在之后讨论。)但这样的实现方式会引起一个附加问题:它让 不同操作之间的组合 变得复杂了。假如我们还定义了一个 query 函数,如下:

query :: DB -> (DB, Int)

然后,我们的主程序需要做如下的事情:在数据库中查询一个值 x x x,然后把 x + 1 x + 1 x+1 写回数据库中。那么,我们的主程序就需要这样编写:

(x, db1) = query db
(ok, db2) = update (db1, (x + 1))

问题就在于,我们不能直接把 query 的返回值加

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值