【问题标题】:Lambda expressions as monads in Scheme?Lambda表达式作为Scheme中的单子?
【发布时间】:2015-05-11 01:42:00
【问题描述】:

我在网上阅读了一些试图向 Scheme 程序员介绍 monad 概念的文档。核心思想是,在非命令式编程中,monads 用于表达“计算流程”,即强制对表达式进行顺序评估。然后它让我印象深刻:既然 lambda 表达式的主体是按顺序计算的,人们应该明白 monad 在 Scheme 中是多余的吗? lambda 体在其他语言(例如 Haskell 或 ML)中的评估方式是否不同?

【问题讨论】:

  • 您阅读的文件可能具有误导性。单子是一个非常抽象的想法,不幸的是,抽象的想法会吸引各种无意的错误信息。我不想尝试自己解释它并可能导致更多的混乱(这就是为什么我不想写一个完整的答案),所以我只是将你链接到一篇广受好评的文章以建立直觉关于单子:"You Could Have Invented Monads! (And Maybe You Already Have.)".
  • 不完全。 Monad 可用于哑测序,但它们更多的是关于每个操作之间发生的事情,而不是测序本身(它们被称为“可编程分号”)。不过,@Tikhon 提到的文章很棒,我绝对会推荐它。
  • 可能感兴趣的人:有一个完整的 monad 解释性文章时间表:wiki.haskell.org/Monad_tutorials_timeline :)。

标签: haskell lambda functional-programming scheme monads


【解决方案1】:

顺序评估

因此,表达式的顺序求值不是 monad 的特征,而是 函数 的特征。从你写f (g x)(Haskell)或(f (g x))(方案)的那一刻起,你已经对g进行了排序,就像在f之前发生的那样。在 Haskell 中,我们使用诸如 (then f g)(bind f (lambda (x) (make-g x))) 之类的排序运算符来执行此操作,但您可以随意执行此操作。

混淆是:monads 至少在 Haskell 中带有一个通用符号,而这个通用符号将语句置于命令式顺序中。从 Scheme 的角度来看,这是一个宏 do,它重写了:

(do
    (put-string "Hey, what's your name?")
    (into x get-line)
    (put-string (string-append "Hi, " x "!")))

(then 
    (put-string "Hey, what's your name?")
    (bind get-line (lambda (x) 
        (put-string (string-append "Hi, " x "!")))))

请注意,lambda 被“扁平化”并替换为一些新符号“into”。 (实际上在 Haskell 中我们会说“from”,但它是一个中缀运算符,而不是前缀运算符。)

什么是 Monad

那么单子,真的吗?好吧,首先,让我们定义它们的词性:单子是形容词,就像形容词“蓝色”一样。我可以有一辆蓝色的马车,或者一个蓝色的轮子。 Monad 是函子,这意味着给定从货车到车轮的功能,我可以为您提供从蓝色货车到其蓝色车轮的功能。所以函数可以“在它们之下运行”。但是单子有两个特殊的属性。首先,如果我们使这个想法异常抽象和形而上学,我们有一个仅适用于“蓝色”的想法:形容词必须能够应用于语言中的任何值 .所以你不仅有蓝色的货车和蓝色的轮子,还有蓝色的功能,蓝色的程序,打印“你好,世界!”到屏幕,蓝色的一切。第二个形容词必须是condensable,因为给定一辆蓝色蓝色马车,你可以给我一辆蓝色马车。

做到这一点的一个形容词是“可以为空的......”。您始终可以获取任何值并基于它生成一个新的可空值:只需开始接受和检测空值!如果您使用的是静态类型语言,默认情况下事物不可为空,那么这很重要。这个形容词是一个函子:给定一个函数,我们只需将 null 转换为 null 以及使用该函数转换的任何其他内容。它是可压缩的,因为如果我们有一个“可空的可空字符串”可以简化为可空字符串:将“null”和“not-null null”值转换为“null”,将“not-null not-null string”转换为“非空字符串”。最后,要将任何字符串表示为可空字符串,请将其转换为“非空字符串”。这称为Maybe monad。它实际上是 Either y monad 的一个特例,它可以包含 y

另一个可以做到这一点的形容词是“...的列表”。要在形容词下应用函数,请将其应用于列表的所有成员。要将列表列表压缩为列表,请连接其元素。要从任何x 创建x 的列表,请将单元素列表返回给我。这被称为“列表单子”。以这种方式编写很像编写列表推导式,实际上与 Clojure 转换器同构,因此您可以免费获得地图和过滤器。

另一个可以做到这一点的形容词是“从 s 到 s 和 a...的函数”。要从任何x 创建其中之一,只需将传入状态与x 配对。要应用函数,只需将其应用到输出对的适当一侧。十分简单。最后,如果您遇到了复杂的s -> (s, s -> (s, a)) 情况,则通过将传入的 s 输入到卷积函数中来构造一个 s -> (s, a),然后将其输出的 s 输入到输出的 s -> (s, a) 中。这为您提供了您需要返回的(s, a)。这称为“State s monad”,因为您可以将“s”设想为一种类型化状态。

另一个可以做到这一点的形容词是“一个返回 a 的程序”。如果你有一个 int,我们可以构造一个什么都不做并返回这个 int 的程序。如果您有一个程序返回一个 int 和一个从 int 到其他东西的函数,我们可以执行该程序,然后将该函数应用于程序的输出。最后,如果您有“一个返回一个返回 int 的程序的程序”,只需形成一个程序,它运行外部程序来计算内部程序,然后运行内部程序。这称为IO x monad。 (与承诺类似:承诺的承诺 x 与承诺的 x 没有太大区别。)

你可以看到这是一个非常广泛的模式,它并不总是清楚地涉及“序列”。

Scheme 与 Haskell 的比较

一旦您了解 Haskell 通过包含程序作为值来描述其所有 I/O 操作,然后将它们组合在一起,您就会意识到基本上我们通过成为宏来执行功能性 I/O语言。宏语言本身不进行任何任何 I/O,但您可以使用它来构建为您进行I/O 的程序。您将它们输入编译器,编译器会生成执行您希望它执行的实际程序。解释器变得有点冒险,因为解释器需要说“如果我看到一个不是程序的值,我会尝试打印它;如果我看到一个程序的值,我会尝试运行该程序,然后打印它。”这意味着命令提示符下的语法与文件中的语法略有不同。

一旦您了解 Haskell 是一个元编程环境,我们将在其中构建程序即值,您就会了解 Haskell 如何进行函数式 I/O,并且您会看到“monads”的可编程语法作为元元编程设计。这种设计比宏(我们在 Haskell 中也有;它被称为“模板 Haskell”)要简单得多,但可以为许多有用的情况完成工作。它本质上是关于在某种程度上重载“a;b;c;d”意味着依赖于 a、b、c 和 d 类型的(常见)最外层形容词。对于 I/O,它的意思是“做 a,然后做 b,然后……”;对于状态,它意味着“将 a 产生的状态插入 b,然后将 b 产生的状态插入 c,然后......”;对于可空对象,它的意思是“做 a,如果不为空,用它做 b,如果不为空,用它做 c……”。对于列表,它是组成列表的笛卡尔积,“以各种方式配对所有内容。”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多