花了两周时间撸了个好玩的 JS 项目,玩转函数式编程 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
icymorn
V2EX    分享创造

花了两周时间撸了个好玩的 JS 项目,玩转函数式编程

  •  
  •   icymorn
    moevis 2015-10-30 10:15:30 +08:00 5588 次点击
    这是一个创建于 3685 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Lambda-Lite-Js

    这是我最近使用 Javascript 实现的一个函数式小语言,我把它叫做 Lambda-Lite-js (简称: LL 语言),满足各位前端程序员在函数式语言中对装逼的需求。先上 demo 。

    示例及在线 demo: http://icymorn.github.io/lambda-lite-js/ (注意每个语句后要有分号,有些语法错误请到控制台看,错误提示还不完善,见谅)
    github: https://github.com/icymorn/lambda-lite-js

    特点

    语法类似 Haskell ,我在其中实现了很多好玩的特性,比如,lambda 演算柯里化函数延迟计算模式匹配Point-Free 风格

    虽说的函数式语言,但是特别提供了中缀运算符的调用,比如 + - * /。支持的类型有: boolean 、 string 、 number 、 list 。

    内置函数:printlengthreverse 等(等后续完善)。

    先来一发阶乘计算。

    let fact n = if n == 1 then 1 else n * (fact n - 1); print (fact 5); 

    现在我不讲语法,先讲一下好玩的特性:

    Point-Free 风格

    函数式语言的函数调用是左结合的,所以很多时候需要大量的括号,这是一种反人类的做法(没错,我说的是 Lisp ),这时候你可以用 $ 来改变当前函数结合顺序

    let double n = n + n; print (double 10); print $ double 10; 

    需要使用现有函数组合成更强大的函数?那么类似 haskell 中的 . 可以做到。计算 n * n + n * n 的式子可以用 n + nn * n 组合起来

    let double = \n -> n + n; let square = \n -> n * n; let func = double . square; print $ func 10; 

    Lambda 演算与不动点组合子

    各位细心的话,可以发现我在上面的示例中用了两种声明函数的方法:

    • \n -> n + n;
    • let double n = n + n;

    前一种是纯的 Lambda 表达式,即匿名函数,有且仅有一个参数,但是可以组合出神奇的效果。比如 Lambda 组合出多参数函数。

    let add = \a -> \b -> a + b; print $ add 1 2; 

    后一种是我添加的语法糖,但是好像更好看一点,支持多参数函数声明(实际还是 lambda 函数组合)下面是一个柯里化的例子:

    let add a b = a + b; let add3 = add 3; print $ add3 4; print $ add3 5; 

    以上通过生成一个新函数 add3 ,固定了 add 函数的第一个参数,最后输出 7 8 ;

    还有更有意思的不动点组合子,由于匿名函数没有名字,通常是没法递归自己的(除非使用 let 命名了)。这时候轮到 z-combinator 出场,由于 ll 语言在调用函数时是 call-by-value 的,所以不能用 y-combinator 。 z 组合子表达形式是这样的 \f -> (\x -> f (\y -> x x y)) (\x -> f (\y -> x x y))

    又一个阶乘函数:

    let z = \f -> (\x -> f (\y -> x x y)) (\x -> f (\y -> x x y)); let makeFact = \g -> \n -> if n < 2 then 1 else n * (g n - 1); let fact = z makeFact; print (fact 5); 

    模式匹配

    haskell 语言中最棒的特性之一就是模式匹配, ll 中也有基本的模式匹配特性,虽然语法不太好看~

    继续用阶乘例子:

    let fact n@1 = 1; let fact n@Number = n * (fact n - 1); print $ fact 5; 

    当参数为 1 , fact 返回 1 ,否则返回 n * (fact n - 1), 这比开头的那个更简洁,不需要再手写 if else then 了。

    使用通配符还能做通用匹配。

    let echo a@Number = print 'Number'; let echo a@String = print 'String'; let echo a@* = print 'Other'; echo 100; echo "hello"; echo true; 

    使用模式匹配需要注意的是, 同名函数的参数长度必须相同, 每个参数的描述都要用 @ 隔开并且有描述符. 同名函数的参数顺序和名字要一样。

    持续完善中...

    欢迎点赞。

    14 条回复    2015-10-31 18:25:51 +08:00
    fo2w
        1
    fo2w  
       2015-10-30 11:18:09 +08:00
    我不光点赞了, 我还粉了.....
    支持同性交友
    irainy
        2
    irainy  
       2015-10-30 11:35:47 +08:00 via iPhone
    手动点赞
    tkisme
        3
    tkisme  
       2015-10-30 11:52:08 +08:00
    为什么不玩回调,闭包之类的
    icymorn
        4
    icymorn  
    OP
       2015-10-30 12:32:13 +08:00
    @tkisme2013
    闭包比较容易嘛,而且在 LL 语言中我自己实现了闭包。回调就更直观了。还有好多更新奇的东西等待发掘。
    icymorn
        5
    icymorn  
    OP
       2015-10-30 12:33:21 +08:00
    @fo2w 感谢支持
    zhy0216
        6
    zhy0216  
       2015-10-30 12:38:39 +08:00
    由于 ll 语言在调用函数时是 call-by-value 的,所以不能用 y-combinator . Scheme 就是 call-by-value 的, 可以用 Y 啊... 这句话没读懂
    icymorn
        7
    icymorn  
    OP
       2015-10-30 12:39:54 +08:00
    @irainy
    被浙大大大 follow 了,受宠若惊,感谢支持。
    icymorn
        8
    icymorn  
    OP
       2015-10-30 13:22:34 +08:00
    @zhy0216

    虽然我并不熟 scheme ,不过我来尝试给你回答一下。在 call-by-value 语言中,参数会被事先计算出来,而 y-combinator 会因此陷入死循环。
    这是标准的 Y = \f.(\x.f(x x)) (\x.f(x x))
    但是在 scheme 中,你会发现其实
    (define Y
    (lambda (h)
    ((lambda (x) (h (lambda (a) ((x x) a))))
    (lambda (x) (h (lambda (a) ((x x) a)))))))
    里面多套了层 lambda ,阻止了进一步的求值,你对比一下我给出的 z combinator ,其实是一样的,只不过大家都习惯性叫 y 而已。
    zado
        9
    zado  
       2015-10-30 13:34:31 +08:00
    楼主一定是 Javascript 高手,我做了一个在线执行 js 的东东,我希望把它做得功能完善一点,可是 js 语言我学得不多,你的代码能不能放在上面执行,如果不能是为什么? http://zxxq.sinaapp.com/zxjb.html 能不能帮我看一下?
    icymorn
        10
    icymorn  
    OP
       2015-10-30 16:31:51 +08:00   1
    @zado
    应该不能的,像 require 和 exports 我都是有重新定义的,才能使我的代码在浏览器和 node.js 中可以用同一份,代码间的相互依赖要改起来也是挺麻烦的。
    amaranthf
        11
    amaranthf  
       2015-10-30 18:53:20 +08:00
    首先人家 lisp 不是函数式语言……另外,括号多么优美!
    icymorn
        12
    icymorn  
    OP
       2015-10-30 19:00:19 +08:00 via Smartisan T1
    @amaranthf
    lisp 都不算函数式语言… 你这定义太严格了吧。
    zhy0216
        13
    zhy0216  
       2015-10-30 21:56:28 +08:00
    @icymorn 确实如此, 涨见识了, thx
    Kabie
        14
    Kabie  
       2015-10-31 18:25:51 +08:00
    …………赶紧看看。。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1071 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 18:17 PVG 02:17 LAX 10:17 JFK 13:17
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86