haskell - 为什么 kleisli 组合需要一个纯值?
问题描述
这是 kleisli 组合的常见实现:
kleisli :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
kleisli = \f g x -> f x >>= g
为什么它不期望一元上下文中的值呢?我确信这是有充分理由的。我只是没有设法看到它。
kleisli' :: Monad m => (a -> m b) -> (b -> m c) -> m a -> m c
kleisli' = \f g x -> x >>= f >>= g
该类型似乎更易于组合,并且return
可以在调用站点上只有纯值的情况下使用。
解决方案
Kleisli 组合实际上是回答常见问题的最简单方法之一:单子有什么用?
我们可以用普通函数做的最有用的事情之一就是组合它们。给定f :: a -> b
and g :: b -> c
,我们可以先执行f
然后g
对结果执行,给我们g . f :: a -> c
。
只要我们只需要使用“普通”函数,那就太棒了。但是一旦我们开始在“现实世界”中编程,如果我们希望我们的语言保持纯粹和引用透明,我们很可能会遇到无法继续使用这些功能的情况。事实上,在这种情况下,其他没有 Haskell 原则性的语言放弃任何纯粹的伪装。考虑这些日常情况:
- 我们的函数
f
有时可能无法返回值。在许多其他语言中,这将通过返回来表示null
,但您不能将其输入g
. (您当然可以进行调整g
以应对null
输入,但这很快就会重复。)
在 Haskell 中,我们没有null
s,我们有Maybe
类型构造函数来显式地表示可能没有值。这意味着f
需要有 type a -> Maybe b
。出于同样的原因g
将有类型。b -> Maybe c
但是在这样做的过程中,我们失去了组合这两个函数的能力,因为我们不能直接将 type 的值提供Maybe b
给需要 type 输入的值b
。
- 的结果
f
可能取决于一些副作用(例如来自用户的输入,或数据库查询的结果)。这在不纯语言中没有问题,但在 Haskell 中,为了保持纯洁性,我们必须以 type 函数的形式实现它a -> IO b
。再一次,g
会以相同的形式结束,b -> IO c
我们已经失去了天真地组合这两个函数的能力。
我相信你可以看到这是怎么回事。在这两种情况下(并且可以很容易地提供更多,每个 monad 一个),我们不得不用 type 中的一个来替换一个简单的 type 函数,a -> b
以a -> m b
解决特定类型的“副作用” - 或者,如果您愿意,适用于函数结果的某种特定类型的“上下文”。这样一来,我们就失去了组合两个函数的能力,而这正是我们在“无副作用”世界中所拥有的。
monad 真正的目的是克服这一点,让我们为这种“不纯函数”恢复一种组合形式。当然,这正是 Kleisli 组合给我们的,一种形式的函数组合,a -> m b
它完全满足我们期望的函数组合的属性(即关联性,以及每种类型的“恒等函数”,这里是return :: a -> m a
)。
您对类型的“不完全组合”的建议(a -> m b) -> (b -> m c) -> (m a -> m c)
通常不会有用,因为结果函数需要一个单子值作为其输入,而在实践中单子值出现的主要方式是作为output s。但是,您仍然可以在需要时执行此操作,只需采用“正确的” Kleisli 组合,并通过>>=
.
推荐阅读
- php - Node js无法通过php从curl请求中读取数据
- mysql - 为什么在使用 go lang 执行 mysql DB 操作时,内部将 sql 转换为准备好的语句样式执行
- vb.net - 如何过滤包含列数据库中指定的所有单词的记录
- python - 如何利用字典计算数据框中的多个平均值
- javascript - 使用 switch case 和过滤/查找/循环对象数组来过滤并找到正确的对象以更新 JavaScript
- python - Python3如何从列表中检索列表
- shell - 在多个目录中执行`make`
- linux - 清漆 ESI 缓存 - 远程 url 返回 404
- reactjs - 在反应中将一些值从一页传递到另一页
- postgresql - Postgres lo_import 从其他表文本数据导入大对象