惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

smallyu的博客

为什么买的加密货币一定要去中心化 RISC-V 虚拟机无法代替 EVM 虚拟机 Polkadot 的 Existential Deposit 机制错在哪里 利息,套利,交易策略,金融市场 LayerZero 从来不挑战比特币的地位 默认参数引起的以太坊节点运行错误 硬件钱包与资产安全 预测市场比币圈更赌场 为什么去中心化的跨链桥不可能实现 我对于 AI 时代的答案 不要投资任何隐私币 区块链技术世界的三大真理 以太坊 AA 钱包的致命问题是什么 基于 AI 语义执行的 MCP 区块链的设计 一个集成 Geth 和 CometBFT 的兼容层 我的加密货币定投策略(二) DeFi 基础: 理解 AMM 定价机制 Go 语言 GMP 调度器的原理是什么 Web3 项目分析计划 对 Psyche Network 项目的分析 continuation 教程: 理解 CPS 如何开发一个比特币符文(Runes)协议 比特币脚本开发教程 我从王垠的计算机科学视频班学到了什么 区块链技术面试题(2025年版) Rust 语言容易让新手困惑的一个“过度优化” Solana 智能合约开发教程 (1) 尝试开发一个最小 EVM 虚拟机 基于 ZK 的链上身份系统设计 一个 Web3 打赏系统的设计 鼓吹 Cursor 的人技术能力都差 关于 Code Review 的礼节 假如启动一个新的以太坊 PoS 网络 发行加密货币的最好方式 所有 BFT 共识的区块链都是中心化的 对 2025 年区块链行业的预测 Restaking 项目的经济难题 如何看懂任意区块链项目的技术架构 为什么不要做以太坊的二次开发 为什么不要做智能合约开发和 DeFi 开发 我的加密货币定投策略(一) PoS 类型的区块链如何处理分叉 Ethereum Casper 为什么需要 EIP-7251 区块链中的 PBFT 不需要第二次投票 开发者的思维方式 发币的核心要点 炒币投资的小 tips 为什么说 PoW 比 PoS 更加去中心化 牛市对普通人来说意味着什么 如何健康地远程工作 为什么比特币不用概率加密函数 程序员的 “服从权威” 心理 区块链技术面试题 如何区分公有链和联盟链 对 Layer 2 项目创业想法的回复 对区块链共识机制的理解 Pebbling Game 鹅卵石游戏 PDP 文件证明的局限性 不要小瞧 ChatGPT 为什么炒币不是一个好主意 一种在区块链上生成随机数的机制 为什么以太坊的私钥计算不可逆 关于以太坊的私钥碰撞 “猜均值的2/3” dApp 游戏设计 Proof of Storage/Space/Replication 的区别 Proofs of Retrievability 文件证明的含义 对 S-PDP 文件证明的示例和解释 我的加密货币交易机器人 对区块链行业的见闻 随机确认块的共识机制 VRF + BFT 共识引起交易失败的问题 为什么要重视编程思想 对 Web 3.0 的理解 GitBook 好用吗? 一种区块链节点存储扩容的方式 一种基于“自我中心主义”的共识机制 链表常见算法题及解析 基于 Multi-Linked List 的区块链设想 理解哈希函数与序列化 联盟链比公有链差在哪儿 在 Dijkstra 算法中保存路径 为什么数字货币使用区块链是政治问题 区块链:下一代数字身份认证体系的基石 网页技术能实现 3D 建模吗? 给区块链一个定义 从 Erlang 开始了解 Actor 模型 一种侧边导航栏的交互方式 Rust 的 ownership 是什么? 浅析 Libra 背后的区块链技术 对区块链的理性认识 Rust 基础语法概述 基于 Java 的爬虫框架 WebCollector Kotlin:简化版的 Scala JavaScript 有关联数组吗? 主流编程语言的异常处理机制 Go 语言基本语法 Scala 语法基础 用 Scala 改写 Java 浅度实践 Python 获取海贼王更新信息 HTML5 音乐可视化
Haskell 中的 Monad 是什么?
2019-11-26 · via smallyu的博客

2019-11-26

第一次听说Monad是在一个Scala Meetup上,后来试着了解Monad的概念,却头疼于Haskell的各种大部头的书和教程。再后来看到阮一峰在2015年发表的《图解 Monad》,虽然清晰易懂,但是脱离了Haskell,图片的表意和语言中的概念对不上。阮一峰的文章译自《Functors, Applicatives, And Monads In Pictures》,我阅读了原文。

前言

计算机程序用于控制计算机进行运算,程序操作的对象是各种不同类型的值,比如数值。这是一个简单的值2

用函数对值进行一些处理,可以返回函数执行的结果,比如:

除了简单的数值类型,值也有可能被包含在一些上下文环境中,组成更复杂的值类型。可以把上下文环境想象成盒子,数值放在盒子里面,这个盒子整体作为一个值,描述为Just 2,也就是带盒子的2

如果对Java有过了解,可以将这个盒子理解为包装类,比如Integer和int,对应带盒子的2和不带盒子的2。

Functors

面对带盒子的2,我们无法直接把+3的函数作用在它上面:

这时需要一个函数fmap来操作。fmap会先从Just 2中取出数值2,然后和3相加,再把结果5放回盒子里,返回Just 5

fmap怎么知道该如何解析Just?换一个其他像Only之类的类型,还能解析吗?所以就需要Functor(函子)来完成定义的的操作。

Functor是一种数据类型:

Functor定义了fmap的行为:

fmap有两个入参和一个出参,入参分别是一个函数和一个带盒子的值,出参是一个带盒子的值,可以这样使用:

fmap (+3) (Just 2)
-- Just 5

回到Haskell,在Haskell的“系统类库”中有一个Functor的实例MaybeMaybe中定义了fmap的行为,指定了面对Just类型的入参时对值进行操作:

instance Functor Maybe where
  fmap func (Just val) = Just (func val)
  fmap func Nothing = Nothing

表达式fmap (+3) (Just 2)的整个过程类似这样:

同理,从Maybe的定义中能看出,如果传入fmap的第二个参数是Nothing,函数将返回Nothing,事实确实如此:

fmap (+3) Nothing
-- Nothing

现在假设一个Java的场景,用户使用工具类Request发起一个向服务器的请求,请求返回的类型是Response,Response是一个实体类,可能包含所需数据data也可能不包含:

Response res = Request.get(url);
if (res.get("data") != null) {
  return res.data;
} else {
  return null;
}

使用Haskell中fmap的写法就变成了:

fmap (get("data")) (Response res)

当然Haskell不存在get("data")这样的写法,可以将由Response获取Response.data的操作封装为函数getData,然后传入fmap作为第一个参数。

Haskell提供了fmap函数的语法糖<$>简化fmap的写法:

getData <$> (Response res)

再来想一个问题,Haskell的函数是如何对列表进行操作的?函数会对列表的每一个元素都进行计算,然后返回列表:

其实列表也是Functions,这是列表的定义:

instance Functor [] where
  fmap = map

Applicatives

Applicatives是另一个概念,我们之前说数据被放在盒子里,如果函数也被放在盒子里呢?

Haskell的系统提供了操作符<*>用于处理盒子里的函数:

例如:

Just (+3) <*> Just 2 == Just 5

使用<*>还可以完成一些有趣的操作,比如分别让列表中的元素*2和+3:

[(*2), (+3)] <*> [1, 2, 3]
-- [2, 4, 6, 4, 5, 6]

Monads

函数的执行是使用带入参函数处理,涉及到三个角色。Functors是被处理的放在盒子里,Applicatives函数放在盒子里,Monads则是将函数的入参放在盒子里。Monads有一个操作符>>=来实现Monads的功能。假设现在有一个函数half的入参是数值,如果是偶数就除以2,否则返回Nothing:

half x = if even x
  then Just (x `div` 2)
  else Nothing

想要给half传一个Just类型的值怎么办?

>>=可以解决这个问题:

Just 3 >>= half
-- Nothing

>>=操作符把Just 3变成了3放在half中进行计算。Monad是一个数据类型,定义了>>=的行为:

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b

这里的Maybe是一个Monad(和上文的Maybe同时存在):

instance Monad Maybe where
  Nothing >>= func = Nothing
  Just val >>= func = func val

>>=还支持链式的操作:

Just 20 >>= half >>= half >>= half
-- Nothing

小结

虽然Haskell的Monad比较有名,但实际上涉及到三个概念FunctorsApplicativesMonads,可能Monad的应用比较广泛一点。在数据处理上,FP并不比OOP高级,逻辑是相似的,只是写法不同。面对同样的问题使用不同的思维方式和表达方式去解决,对应了不同的编程思想和编程范式。世界上有很多精妙的理论等待我们探索。