Lambda表达式作为Scheme中的monad?

Ale*_* M. 1 monads lambda scheme haskell functional-programming

我正在网上阅读一些试图为Scheme程序员提出monad概念的文档.核心思想是,在非命令式编程中,monad用于表示"计算流程",即强制执行表达式的顺序评估.然后它让我感到震惊:因为lambda表达式的主体是按顺序计算的,所以应该理解monad在Scheme中是多余的吗?lambda实体在其他语言中的评估方式是否不同(例如Haskell或ML)?

CR *_*ost 9

顺序评估

因此,表达式的顺序评估不是monad的特征,而是函数的特征.从你写的那一刻f (g x)(哈斯克尔)或(f (g x))(计划)已测序g的发生之前f.在Haskell中,我们使用像运算符这样的运算符执行此操作(then f g),(bind f (lambda (x) (make-g x)))但您可以按照自己的意愿执行此操作.

令人困惑的是:monad 至少在Haskell中带有通用符号,而这种通用符号将语句置于命令式顺序中.从Scheme的角度来看,这是一个do重写的宏:

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

至

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

请注意,lambdas被"展平"并用一些新符号"into"替换.(实际上在Haskell中我们会说"from"但它是一个中缀运算符,而不是前缀运算符.)

Monad是什么

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

这样做的一个形容词是"可以为空的......".您可以随时获取任何值并根据它生成新的可空值:只需开始接受并检测空值!如果你是一个静态类型的语言,默认情况下事物不可为空,那么这很重要.这个形容词是一个仿函数:给定一个函数,我们只需将null转换为null以及我们用函数转换的任何其他东西.它是可以缩小的,因为如果我们有一个可以为可空的字符串的"可空的可空字符串":将"null"和"not-null null"值取为"null",将"not-null not-null string"取为"非空字符串".最后,要将任何字符串表示为可空字符串,请将其转换为"not-null string".这被称为Maybemonad.它实际上是Either ymonad的一个专门案例,它可以拥有一个y.

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

另一个形容词是"从s到s和a ......的函数".要从任何一个创建其中一个x,只需将传入状态与x.要应用函数,只需将其应用于输出对的适当侧.十分简单.最后,如果你有复杂的s -> (s, s -> (s, a))情况,s -> (s, a)通过将传入的s馈送到复杂的函数来构造一个,从中获取s并将其馈送到它出来的s s -> (s, a).这为您提供了(s, a)返回所需的信息.这被称为" State smonad",因为你可以将"s"视为一种打字状态.

另一个形容词是"返回一个程序".如果你有一个int,我们可以构造一个什么都不做的程序并返回那个int.如果你有一个程序从int返回一个int和一个函数,我们可以执行该程序,然后将该函数应用于程序的输出.最后,如果你有一个"返回一个返回int的程序的程序",只需要形成一个运行外部程序来计算内部程序的程序,然后运行内部程序.这被称为IO xmonad.(与承诺类似:承诺的承诺x与承诺的x没有太大区别.)

你可以看到它是一个非常广泛的模式,它并不总是明显涉及"序列".

将方案与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,它意味着"做一个,然后做b,然后......"; 对于状态,它意味着"将由a生成的状态转换为b,然后将b生成的状态转换为c,然后......"; 对于nullables,它意味着"做一个,如果那不是null就用它来做b,如果那不是null就用它来做c ......".对于列表,它是成分列表的笛卡尔积,"在所有方面配对一切".