Raj*_*jat 19 javascript monads
所以我想了解JavaScript中monad有用的实际案例.
我在JavaScript上阅读了关于Monads的文章,并了解jQuery是其使用的一个例子.但除了"链接"模式,使用Monads在前端工程中可以有效解决哪些其他问题?
参考:
http://importantshock.wordpress.com/2009/01/18/jquery-is-a-monad/
http://igstan.ro/posts/2011-05-02-understanding-monads-with-javascript.html
小智 8
这是我尝试为您可能从未在其他地方找到过的monads入门者做出的贡献。
(IMO,在没有任何上下文和合理化的情况下引入“ Monad法则”仅是毫无用处的分类,并且是理解该概念的危险。不用担心,我将在本文的后面做此工作。)
在大多数情况下,我们有许多种编程的构建块,例如对象,函数,列表等。
尽管拥有各种各样的编程块似乎是自然法则,并且出于实际目的进行灵活编程是不可避免的,但事实是,拥有各种各样的编程块是编程环境污染的主要来源之一。
通过使用各种块来构建块是一项复杂的任务。在每种情况下,程序员都必须非常明智地从各种块中选择一个块,并且很长一段时间内,他将失败。
因此,不建议根据情况选择块的种类,相反,始终使用普遍标准化的特定预选块是一个很好的习惯。
实际上,这种智慧在当今的PC世界中很普遍。
USB是通用串行总线的简称,是一种工业标准,旨在定义电缆,连接器和协议,用于个人计算机及其外围设备之间的连接,通信和电源。
获得设计良好的通用标准化构建基块可以消除许多问题。
选择对象作为通用标准化的构建块时,程序员将准备一个包含成员值和函数的基类,并且为了获得块的变体,将使用继承。
OOP的想法通常是通过使用现实世界的物理对象来解释的,并且范式本身在数学抽象上是薄弱的。
例如,函数(或方法)从属于对象,而函数不必是一流的对象,这是理所当然的,因为范式最初选择的对象是其设计良好的通用标准化构造块。
功能在哪里是对象的从属实体(作为标准化的构建基块,并且两个角色完全不同)的观点来自物理世界中的工程学意义。而不是编程实际所在的数学抽象。
OOP的根本问题只是对象原来不是设计良好的通用标准化构件。具有强大数学背景的函数式编程或monad是更好的选择。
函数式编程就是关于函数的组合。
说起来很容易,但这是编程历史上的成就。
我不想研究悠久的编程历史,而是分享自己的个人历史。
从1.0版开始,我曾经是C#(OOP)程序员,总的来说,我很满意,但是觉得有些不对劲,但是不知道那是什么。
后来我成为了JavaScript程序员,在早期,我曾经这样写:
function add1(a) {
return a + 1;
}
Run Code Online (Sandbox Code Playgroud)
有一天,我读了一些网络文章,说“在JavaScript中,函数也是一个值”。
这个事实令我感到惊讶,并突破了我的编程技能。
在那之前,对我来说,很明显,价值就是价值,而功能就是功能。两者都是在不同领域中绝对不同的实体。
当然,C#1.0已经实现了委托,并且我稍微理解这与事件的内部机制有关。毕竟,C#一直是一种主要的OOP语言,至少在版本1.0中,对于函数式编程而言这是非常丑陋的。
在JavaScript中,函数也是一个值。由于JavaScript的函数是一流的对象,因此我可以定义一个函数,该函数可以将其他函数作为参数,也可以将它们作为结果返回。
所以,现在,我这样写:
const add1 = x => x + 1;
const add2 = x => x + 2;
[1, 2, 3].map(add1); //[2,3,4]
[1, 2, 3].map(add2); //[3,4,5]
Run Code Online (Sandbox Code Playgroud)
要么
const plus = (x) => (y => x + y);
plus(1)(5); //6
Run Code Online (Sandbox Code Playgroud)
实际上,这是我在C#编程中急需的,这是我感到非常错误的事情。
这称为函数组合,这是释放编程约束的真正秘密。
因此,JavaScript的功能是一个一流的对象,并且它似乎是一个设计良好的通用标准化构建块,从现在开始,我们将其称为“高度可组合的单元”。
一个功能是BEFORE => AFTER。
基本思想是组成功能。
当您专注于功能组成时,您只关心的各种组成BEFORE => AFTER。
当您专注于功能组合时,您应该忘记流程图,它从代码的顶部到底部流动,有时甚至循环。
流程图编码被称为命令式编程,通常来说,它有错误并且过于复杂。OOP倾向于成为这种样式。
另一方面,函数式编程会自动将编程风格引向Declarative_programming,并且通常来说,它不是bug也不容易调试。
流程更难追踪和控制,但是组合更容易追踪和控制。程序员不应控制流程,而应组成功能。
顺便说一句,我将不在这里使用Haskell代码。
对于大多数人而言,理解单子事物的主要障碍是
这是“先吃鸡还是先吃鸡蛋?” 问题。确保避免。
话虽如此,正如我在本文开头所述,要分享Monad的知识,首先引用“ Monad法律”似乎也很荒谬。
人们只能根据已经知道的知识来学习。
因此,让我们回到JavaScript代码。
函数似乎是高度可组合的单元,但是呢?
console.log("Hello world!");
Run Code Online (Sandbox Code Playgroud)
这是最简单的JS代码之一,并且肯定是一个函数。
在ChromeBrowser上按F12键,然后将代码复制并粘贴到开发人员控制台上。
Hello world!
undefined
Run Code Online (Sandbox Code Playgroud)
好的,代码已完成显示“ Hello world!”的任务。但是,在控制台上,该console.log函数的返回值为undefined。
组成职能时,情况令人不舒服;令人不舒服的功能
另一方面,有一个舒适的功能。让我们研究以下代码:
Hello world!
undefined
Run Code Online (Sandbox Code Playgroud)
JavaScript中的数组在函数式编程世界中表现良好。
const add1 = x => x + 1;
[1, 2, 3].map(add1); //[2,3,4]
Run Code Online (Sandbox Code Playgroud)
表示:
Array Function=> Array
函数的输入和输出是同一类型:Array。
整个数学结构是相同的BEFORE => AFTER。
一致性和身份的本质是美丽的。
与USB接口的有趣相似之处自然会带来一个想法:
Array Function=> Array Function=> Array Function=> Array...
在JavaScript代码中:
[1, 2, 3].map(add1) //[2,3,4]
Run Code Online (Sandbox Code Playgroud)
该代码建议,一旦您进入Array域,退出将始终是Array域,因此在某种意义上没有退出。
由于数组领域是一个独立的世界,因此可以在函数式编程中做类似代数的事情。
当我们有:
[1, 2, 3]
.map(add1) //[2,3,4]
.map(add1) //[3,4,5]
.map(add1);//[4,5,6]
Run Code Online (Sandbox Code Playgroud)
考虑到.map(F)JavaScript数组特定的语法,例如,可以利用Babel之类的编译器,将其替换为更简洁的语法。
因此,替换.map(F)为*F:
Array.map(F).map(F).map(F)...
Run Code Online (Sandbox Code Playgroud)
这看起来像代数。
获得高度可组合的单元后,程序员可以编写代数这样的代码,这意味着意义重大,值得认真研究。
在代数中
a
= 0+a
= 0+0+a
= 0+0+0+a
Run Code Online (Sandbox Code Playgroud)
要么
a
= 1*a
= 1*1*a
= 1*1*1*a
Run Code Online (Sandbox Code Playgroud)
0 在+(加法)操作中,
a + 0 = a //right identity
0 + a = a //left identity
Run Code Online (Sandbox Code Playgroud)
1 在*(乘法)运算中,
a ? 1 = a //right identity
1 ? a = a //left identity
Run Code Online (Sandbox Code Playgroud)
被称为身份元素。
在代数中
1 + 2 + 3 = 1 + 2 + 3
(1+2) + 3 = 1 + (2+3)
3 + 3 = 1 + 5
6 = 6
Run Code Online (Sandbox Code Playgroud)
被称为关联财产
number + number = number
number * number = number
string + string = string
"Hello" + " " + "world" + "!"
= "Hello world" + "!"
= "Hello "+ "world!"
Run Code Online (Sandbox Code Playgroud)
也是关联的,标识元素是""。
那么,函数式编程中的标识元素是什么?
就像是:
identityF * f = f = f * identityF
Run Code Online (Sandbox Code Playgroud)
函数编程中的关联属性是什么?
const add1 = x => x + 1;
const add2 = x => x + 2;
const add3 = x => x + 2;
Run Code Online (Sandbox Code Playgroud)
就像是:
add1 * add2 * add3
= (add1 * add2) * add3
= add1 * (add2 * add3)
Run Code Online (Sandbox Code Playgroud)
要么
?(add1)(add2)(add3) = (add1)(add2)(add3)
((add1)(add2))(add3) = (add1)((add2)(add3))
(add3)(add3) = (add1)(add5)
? (add6) = (add6)
Run Code Online (Sandbox Code Playgroud)
函数式编程全部与函数组成有关。
我们在函数式编程中需要的是
function * function = function
Run Code Online (Sandbox Code Playgroud)
当然,由于每种语言的语法限制,在JavaScript(或其他语言)中,我们无法编写上面的确切形式。
实际上,我们可以拥有“代数JavaScript规范”(JavaScript中常见代数结构的互操作性规范)
那么JavaScript数组就是所谓的Monad吗?
不,但是很近。JavaScript数组可以归类为Functor。
Monad是Functor的一种特殊形式,具有一些额外的性质(适用更多规则)。
函子仍然是高度可组合的单元之一。
因此,我们正在接近Monad。让我们走得更远。
现在,我们知道JavaScript数组是可组合的单元之一,可以至少在一定程度上进行代数运算。
那么除了数组以外的JavaScript值又如何呢?函数呢?
学习并遵循代数JavaScript规范,很容易尝试实现各种可组合单元,包括Functor或Monad,这有什么意义?
毕竟,它们只是数学结构的分类表,盲目地遵循规范是没有意义的。
关键是要获得一个高度可组合的单元,领域是独立的。这是唯一需要满足的规范。
因此,这是问题的建立:
实现一个数学结构,该结构生成一个独立的领域,并查看其发展过程。
一切都很好,我将从头开始,但是我已经有一个很好的模型可供参考。
JavaScript数组
Array.map(F).map(F).map(F)...
Run Code Online (Sandbox Code Playgroud)
代替Array领域,让我们M像这样创建我的原始领域:
M.map(F).map(F).map(F)...
Run Code Online (Sandbox Code Playgroud)
我认为Array.map这不是一个简洁的语法,M它本身是一个函数:
M(F)(F)(F)...
Run Code Online (Sandbox Code Playgroud)
好吧,总是使用统一标准化的某些预选块是一个很好的准则。那是开始的想法,所以大概F也应该是M:
M(M)(M)(M)...
Run Code Online (Sandbox Code Playgroud)
嗯,这是什么意思?
所以,这是我的疯狂主意。
在函数式编程中,任何函数也是一流的对象,这就是突破。因此,当我将任何值/对象/函数解释为时M,将有另一个突破。
就像说“任何值都是数组!”一样,这很疯狂。
确切地说,如果它在JavaScript领域中是疯狂的,但如果它在Array的独立领域中是合法的。
因此,我将设计原始域M将任何裸值/对象/函数视为M
例如,在M领域中,当5找到裸值:时,将其解释为M(5)。
换句话说,只要在M领域中,程序员就不必编写,M(5)因为它5被隐式解释为M(5)。
因此,在M领域中:
5
= M(5)
= M(M(5))
= M(M(M(5)))
...
Run Code Online (Sandbox Code Playgroud)
结果,我发现它M是透明的,M应该成为领域中的标识元素。
正如我一直强调的那样,函数式编程完全是关于组合函数的。
函数的组成与函数编程相关。
该M应灵活写入撰写功能:
Array*F*F*F...
Run Code Online (Sandbox Code Playgroud)
另外,高阶函数的组成:
a
= 0+a
= 0+0+a
= 0+0+0+a
Run Code Online (Sandbox Code Playgroud)
这是的实现M:
a
= 1*a
= 1*1*a
= 1*1*1*a
Run Code Online (Sandbox Code Playgroud)
记录功能:
a + 0 = a //right identity
0 + a = a //left identity
Run Code Online (Sandbox Code Playgroud)
测试代码:
a ? 1 = a //right identity
1 ? a = a //left identity
Run Code Online (Sandbox Code Playgroud)
输出:
test
test
------
[ 1 ]
5
99
------
[ 2, 3, 4 ]
------
11
12
13
12
13
------
6
6
6
------
[ 1, 2, 3 ]
[ 2, 3, 4 ]
left identity M(a)(f) = f(a)
8
right identity M = M(M)
{ [Function: M] val: [Function] }
{ [Function: M] val: [Function] }
identity
9
9
homomorphism
101
101
interchange
4
4
associativity
12
12
Run Code Online (Sandbox Code Playgroud)
好,工作了
M 是函数编程中高度可组合的单元。
那么,这就是所谓的Monad吗?
是。
https://github.com/fantasyland/fantasy-land#monad
左恒等式M(a)(f)= f(a)单子
实现Monad规范的值还必须实现Applicative和Chain规范。1.
M.of(a).chain(f)等同于f(a)(左身份)2.m.chain(M.of)等同于m(右身份)
1 + 2 + 3 = 1 + 2 + 3
(1+2) + 3 = 1 + (2+3)
3 + 3 = 1 + 5
6 = 6
Run Code Online (Sandbox Code Playgroud)
右身份M = M(M)
number + number = number
number * number = number
string + string = string
"Hello" + " " + "world" + "!"
= "Hello world" + "!"
= "Hello "+ "world!"
Run Code Online (Sandbox Code Playgroud)
身份适用性
实现应用规范的值也必须实现应用规范。1.
v.ap(A.of(x => x))等同于v(identity)2.A.of(x).ap(A.of(f))等同于A.of(f(x))(同态)3.A.of(y).ap(u)等同于u.ap(A.of(f => f(y)))(互换)
identityF * f = f = f * identityF
Run Code Online (Sandbox Code Playgroud)
同态
const add1 = x => x + 1;
const add2 = x => x + 2;
const add3 = x => x + 2;
Run Code Online (Sandbox Code Playgroud)
互换
add1 * add2 * add3
= (add1 * add2) * add3
= add1 * (add2 * add3)
Run Code Online (Sandbox Code Playgroud)
关联性链
实现Chain规范的值还必须实现Apply规范。1.
m.chain(f).chain(g)等于m.chain(x => f(x).chain(g))(缔合性)
?(add1)(add2)(add3) = (add1)(add2)(add3)
((add1)(add2))(add3) = (add1)((add2)(add3))
(add3)(add3) = (add1)(add5)
? (add6) = (add6)
Run Code Online (Sandbox Code Playgroud)
好吧,我认为第一篇文章非常精彩且非常详细.它描述了JQuery及其monad性质解决的许多问题.
这也是JQuery对很多开发人员如此直观的原因,因为它简单地包装了我们所知道的并且不会尝试重新发明HTML.
更不用说它在引用null时会节省很多错误.如果我没有带id的元素guy,那么运行$("#guy").text("I am not here")不会在JQuery中导致错误.
JQuery轻松地围绕DOM元素包装,允许在原始JS和JQuery的界面之间往返.这允许开发人员按照自己的进度学习JQuery,而不是一次性重写整个代码.
当JQuery使用参数提供回调时,它使用DOM对象而不是JQuery的包装器.这允许第三方轻松地与JQuery集成,因为他们不需要依赖JQuery.例如,假设我编写了一个使用原始JavaScript绘制红色文本的函数.function paintRed(element){element.style.color="red"} - 我可以轻松地将此函数作为回调函数传递给JQuery函数.