return :: a -> m a
bind :: m a -> (a -> m b) -> m b
我们尝试定义一个用于表示计算的泛型类型 m,它接受两个类型参数 b(break) 和 c(continue),其中 c 表示预期结果的类型,b 表示意外退出的类型。据此改写 return 的签名:
m :: * -> * -> *
return :: c -> m b c
这里的 return 把 c 类型的预期结果转换为 m,因此也可以称作 from_output。对应地,增加一个函数把意外结果转换为 m。我们这里不把它定义为 m b c 上的方法,因为一个意外结果 b 转换为 m 的过程对于计算结果的类型应该可以泛化。
from_residual :: b -> m b t
由于 m 表示的计算可能是预期的(c)或者意外的(b),我们创建这两种类型的一个简易 sum type ControlFlow,并引入一个 m b c 的方法 branch,用于将 m 转换成为这两个类型之一。m 比我们的 ControlFlow 可能要复杂得多。
data ControlFlow b c
= Break b
| Continue c
branch :: m b c -> ControlFlow b c
据此,我们可以导出 bind。这里的 from_residual 把 b 转换成了新的 m b c' 类型。如果 b 有对应的 from_residual 实现,那么我们的 bind 就可以被定义为 m b c 的导出方法。
bind :: m b c -> (c -> m b c') -> m b c'
bind val compute = case branch val of
Continue output -> compute output
Break residual -> from_residual residual
以上我们描述的 m b c 恰好对应 Rust 的 Try 特征,return 和 branch 刚好就是 Rust Try 的两个方法,而 b 上的 from_residual 刚好就是 FromResidual 特征的方法:
title: Rust Try 和 Haskell Monad date: 2024-08-24T20:00:00+08:00 category: comp tags:
rust
你说得对,但是单子是——后面忘了。同时,逐步发觉 Rust Try 的真相。
在学 Haskell 前曾经写过一篇关于 Rust
Try
特征的文章, 意识到 Rust 的Try
特征其实描述了 HaskellMonad
类的一个特例。根据定义,实现一个 Haskell
Monad
需要实现两个方法:我们尝试定义一个用于表示计算的泛型类型
m
,它接受两个类型参数b
(break
) 和c
(continue
),其中c
表示预期结果的类型,b
表示意外退出的类型。据此改写return
的签名:这里的
return
把c
类型的预期结果转换为m
,因此也可以称作from_output
。对应地,增加一个函数把意外结果转换为m
。我们这里不把它定义为m b c
上的方法,因为一个意外结果b
转换为m
的过程对于计算结果的类型应该可以泛化。由于
m
表示的计算可能是预期的(c
)或者意外的(b
),我们创建这两种类型的一个简易 sum typeControlFlow
,并引入一个m b c
的方法branch
,用于将m
转换成为这两个类型之一。m
比我们的ControlFlow
可能要复杂得多。据此,我们可以导出
bind
。这里的from_residual
把b
转换成了新的m b c'
类型。如果b
有对应的from_residual
实现,那么我们的bind
就可以被定义为m b c
的导出方法。以上我们描述的
m b c
恰好对应 Rust 的Try
特征,return
和branch
刚好就是 RustTry
的两个方法,而b
上的from_residual
刚好就是FromResidual
特征的方法:同时,
return
加上我们导出的bind
恰好对应 HaskellMonad
的定义。和Monad
的区别是,我们定义的Try
的 kind 是* -> * -> *
而不是更一般的* -> *
,需要显示指定一个类型参数b
。Try
特征对应的?
和try
块也一定程度上对应了 Haskell 为Monad
设计的do
表达式: