对多范式语言有用的 monad

2024-01-19

当我理解了 monad 在 c++/python 中的用途后,我终于开始了解 monad 了,它允许您将以下形式的函数链接在一起T -> Generic<U>。例如,如果您有

readfile = [](string s) -> optional<string> {...};
http_request = [](string s) -> optional<int> {...};
inverse = [](int i) -> optional<int> {...}

然后绑定>>= :: optional<T> -> (T -> optional<U>) -> optional<U>具有完全正确的签名以允许您编写这些函数

optional{"myfile"} >>= readfile >>= http_request >>= inverse;
// or in haskell
read_request_inverse = (>>= inverse) . (>>= http_request) . readfile

而不是手动写出短路条件

[]() -> optional<int>
{
    auto message = readfile("myfile");
    if (!message)
        return nullopt
    auto number = http_request(*message)
    if (!number)
        return nullopt
    return inverse(*number)
}

我认为让我困惑的是没有区分链式绑定和嵌套绑定(Bartosz Milewski 演示了 C++ 范围 https://bartoszmilewski.com/2014/10/17/c-ranges-are-pure-monadic-goodness/)并分别理解它们

auto triples =
  for_each(ints(1), [](int z) {
    return for_each(ints(1, z), [=](int x) {
      return for_each(ints(x, z), [=](int y) {
        return yield_if(x*x + y*y == z*z, std::make_tuple(x, y, z));
      });
    });
  });

这只是列表理解

triples = [(x, y, z) | z <- [1..]
                     , x <- [1..z]
                     , y <- [x..z]
                     , x^2 + y^2 == z^2]

另外,我相信我被这样一个事实绊倒了:读者、作者和状态单子只是试图逆向工程副作用(我不完全确定这不会重新引入不纯过程/子例程的问题),所以在多范式语言中没有用处,在多范式语言中您只能产生真正的(合理限制的)副作用。

So monadic optional<T>/result<T,E> seems highly useful in c++/python/rust, and monadic ranges/lists might be useful solving coding challenges but not really for real life problems (edit: I've actually started using flatmap/views::for_each it a lot for simplifying "business logic").

所以我的问题是是否还有其他 monad 的例子可以证明 monad 在多范式语言中的有用性?


充分利用 monad 的编程语言当然是函数式编程语言。您在问题中使用 Haskell,但 monad 在 OCaml 中也很有用。

这种编程模式有用的第一个例子是Nix https://en.wikipedia.org/wiki/Nix_package_manager。 Nix 是一个包管理器,它使用用 nix 编写的脚本(是的,与术语有一些重叠)来描述打包以及其他内容。如果您想知道,是的,nix 是一种纯粹的、惰性的函数式编程语言。它甚至有一个完整的操作系统,其配置是用 nix 编写的,称为NixOS https://en.wikipedia.org/wiki/NixOS。打包应用程序时,单子模式经常出现,尽管方式相当复杂。这个想法是,包本身是 monad,有一个函子,允许您链接包的修改。在伪代码中,保留您的符号,它可能类似于package >>= apply_patch >>= change_version >>= add_tests.

另一个例子是Rust https://www.rust-lang.org/。与 Haskell、OCaml 或 nix 相反,Rust 不是一种函数式编程语言(更多的是一种系统编程语言),但它具有函数式编程语言的许多方面及其许多模式,包括option例如,在 Rust 中,称为Option<T>(通用的T)。除了基本的方法之外,Rust 还提供了几种方法map要处理的函子Option<T>对象,所以典型的 Rust 代码可能看起来像(假设a: Option<T>)

a
  .map(|x| ...)
  .filter(|x| ...)
  .and_then(|x| ...)
  .or_default()

然而,Rust 并不完全像 Haskell 那样处理 monad,因为在 Rust 中,与 monad 兼容的函数(即does_something: fn(U) -> Option<T>)实际上会解压 monad 包含的值,直接对该值进行操作,然后重新包装它们。这很简单?运算符,它只是宏的语法糖(try!一)。用法如下,如果a: Option<T>.

fn do_something_with_a(a: Option<T>) -> Option<U> {
  let b = do_something_with_a_value(a?);
  Some(b)
}

在这里,?运营商首先采取行动match结束a。如果它是一个None,然后函数立即返回None。否则,它是一个Some(x), and x被返回。也就是说,这个?运算符的行为非常像>>=,除了它不采取闭包,并运行它x if a is Some(x)。相反,它会通过让当前函数直接操作来使当前函数就是该闭包x.

到目前为止,一切都很好。但是 Rust 还有其他具有这种单子模式的预定义类型,这些类型不在您原来的问题中。例如,Result<T, E>。在 Rust 中,你通常想要避免throw, to panic!, to raise,或者你想怎么称呼它就怎么称呼它。这不是异常管理的方式。相反,如果一个函数返回一个类型T可能会抛出错误,类型为E,那么它应该返回Result<T, E>。它(正如您可能想象的那样)简单地定义为枚举,就像Option<T> was:

enum Result<T, E> {
  Ok(T),
  Err(E),
}

并且,就像Option<T>,Rust 提供了一个基本函子来扮演这种类型,即map (ie. my_result.map(|x| ...))。然而,就像Option,它还提供了大量其他方法来使用Result对象,但有一种特别惯用的方法:?运算符(是的,再次)!就像选项一样,它的作用如下:

  • 如果对象具有以下形式Ok(x),那么它的值为x;
  • 否则,它的形式是Err(y),它将从当前函数返回Err(y).

实际上,?甚至比这更聪明,因为你的函数可能会调用几个返回的函数Result<_, _>,但具有不同的错误类型。例如,如果您尝试读取文件,则可能会收到 IO 错误。然后你尝试解析一些数据,你可能会得到一个解析器错误。然后你做一些算术,你可能会得到算术错误。为了解决这个问题,你的函数应该返回一个足够通用的错误,以便?可能会在您可能得到的错误(IO、解析、算术……)和您返回的错误之间执行转换。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对多范式语言有用的 monad 的相关文章

  • 对多范式语言有用的 monad

    当我理解了 monad 在 c python 中的用途后 我终于开始了解 monad 了 它允许您将以下形式的函数链接在一起T gt Generic u 例如 如果您有 readfile string s gt optional u
  • Haskell —— 是否有元组的 monad 序列函数?

    假设我有一个类型的值Monad m gt m a m a 我想对这对进行 排序 以创建类型的值Monad m gt m a a 它以与 序列 函数相同的方式组合两个值的单子上下文 是否有一些标准功能或标准方法可以做到这一点 这个操作还有意义
  • 从分类角度来说,FP 中的 monad 是什么?

    每当有人承诺 解释单子 时 我的兴趣就会被激起 但当所谓的 解释 是一长串例子时 我的兴趣就会被挫败感所取代 最后是一些即兴的评论 即 深奥的数学理论 背后的 数学理论 想法 目前太复杂 无法解释 现在我要求相反 我对范畴论有扎实的掌握 而
  • 如何实现index-core风格的索引状态monad?

    我试图理解中的索引单子index core http hackage haskell org package index core风格 我陷入了一个悖论 即在构建一些示例之前我无法理解原理 而在理解原理之前我无法构建示例 我正在尝试构建一个
  • 将 IO 输出收集到列表中

    我怎样才能发出多个呼叫SDL pollEvent IO Event http hackage haskell org packages archive SDL 0 6 2 doc html Graphics UI SDL Events ht
  • 扁平列表和免费 monad

    我试图说服自己 List monad 具有平面列表 列表串联和按元素映射的列表 不是一个自由 monad 准确地说 是与某个函子 T 关联的自由 monad 据我了解 我应该能够通过以下方式实现这一目标 首先在 monad 列表中找到常用运
  • underscore.js 中的 chain 函数是否创建了一个 monad?

    In the chain文档 http underscorejs org chaining你发现 Calling chain在包装对象上将导致所有未来的方法调用 也返回包装的对象 当你完成后 计算 使用value检索最终值 也是如此chai
  • 展平单子栈

    所以我的第一个严肃的 haskell 项目中到处都有这样的代码 f MonadTrans t gt ExceptT t StateT A B C f do mapExceptT lift do lift do lift do r lt re
  • 在 Scala 中处理 monad 时出错?尝试与验证

    scalaz Validation据说比Trymonad 因为它会累积错误 有没有什么场合你可以选择Try over scalaz Validation or scalaz 支持的最重要论据Try是它在标准库中 这也是used在标准库中 例
  • 从 Cats 理解并行类型类

    有一个类型类叫做Parallel in Cats 此类的目的是为一些不支持开箱即用并行计算的 monad 提供并行计算 例如Either例如 我知道Monad用于相关计算 因此需要顺序执行 Applicative用于独立计算 因此此类计算可
  • 如何用bind来定义apply?

    在 Haskell 中 Applicatives 被认为比 Functor 更强 这意味着我们可以使用 Applicative 来定义 Functor 例如 Functor fmap a gt b gt f a gt f b fmap f
  • 将 A => M[B] 转换为 M[A => B]

    对于一个单子M 是否可以转A gt M B into M A gt B 我尝试过遵循这些类型 但没有成功 这让我认为这是不可能的 但我想我还是会问 另外 搜索 Hooglea gt m b gt m a gt b 没有返回任何东西 所以我没
  • “实例显示状态”无法编译

    这是我试图弄清楚的 State Monad 代码 data State a State Int gt a Int instance Monad State where return x State c gt x c State m gt g
  • GHC 可以为 monad 转换器派生 Functor 和 Applicative 实例吗?

    我正在尝试实施MaybeT本着mtl图书馆 使用这个非编译解决方案 LANGUAGE FlexibleInstances MultiParamTypeClasses UndecidableInstances import Control M
  • 使用 Reader Monad 进行依赖注入

    我最近看到了谈话极其简单的依赖注入 http www youtube com watch v ZasXwtTRkio and 无需体操的依赖注入 http vimeo com 44502327关于 Monads 的 DI 并留下了深刻的印象
  • 在一元上下文中使用 Data.Map

    我正在操作的地图具有单子键 类型为IO Double 我需要使用findMax在这张地图上 我可以用吗liftM为了这 Map findMax Map fromList f x X f y Y f z Z Here f x有类型IO Dou
  • Haskell 中的所有图形和 Web 库是如何实现的?

    我才开始学习Haskell 我读到它是一种纯函数式语言 其中的所有内容都是不可变的 因此 输入输出 写入和读取数据库之类的事情会导致状态的可变性 我知道 Haskell 中有一种叫做 monad 的东西 它允许在 Haskell 中使用命令
  • 为什么 Vector[Option[Int]] 上的 flatMap 其映射器函数结果不是 Vector[Option[Int]] 有效?

    例如 Vector Some 1 Some 2 Some 3 None flatMap n gt n 产生一个Vector 1 2 3 而不是给出错误 正如我在其他语言中看到的那样 flatMap当你有一个产生嵌套的映射器函数时使用 所以我
  • Haskell 中的随机枢轴快速排序

    是否有可能在 Haskell 中实现快速排序 使用 RANDOM PIVOT 但仍然有一个简单的Ord a gt a gt a 签名 我开始了解 Monad 目前 我将 monad 解释为某种 命令模式 这对于 IO 非常有用 所以 我知道
  • 为什么 Haskell 没有 I Monad(仅用于输入,与 IO monad 不同)?

    从概念上讲 执行输出的计算似乎与仅执行输入的计算有很大不同 从某种意义上说 后者更为纯粹 就我而言 我希望有一种方法将程序中仅输入的部分与可能实际写出内容的部分分开 那么 为什么没有输入只有 Monad 呢 为什么拥有一个 I monad

随机推荐