
这是我最近使用 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 。
内置函数:print,length、reverse 等(等后续完善)。
先来一发阶乘计算。
let fact n = if n == 1 then 1 else n * (fact n - 1); print (fact 5); 现在我不讲语法,先讲一下好玩的特性:
函数式语言的函数调用是左结合的,所以很多时候需要大量的括号,这是一种反人类的做法(没错,我说的是 Lisp ),这时候你可以用 $ 来改变当前函数结合顺序
let double n = n + n; print (double 10); print $ double 10; 需要使用现有函数组合成更强大的函数?那么类似 haskell 中的 . 可以做到。计算 n * n + n * n 的式子可以用 n + n 和 n * n 组合起来
let double = \n -> n + n; let square = \n -> n * n; let func = double . square; print $ func 10; 各位细心的话,可以发现我在上面的示例中用了两种声明函数的方法:
前一种是纯的 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; 使用模式匹配需要注意的是, 同名函数的参数长度必须相同, 每个参数的描述都要用 @ 隔开并且有描述符. 同名函数的参数顺序和名字要一样。
持续完善中...
欢迎点赞。
1 fo2w 2015-10-30 11:18:09 +08:00 我不光点赞了, 我还粉了..... 支持同性交友 |
2 irainy 2015-10-30 11:35:47 +08:00 via iPhone 手动点赞 |
3 tkisme 2015-10-30 11:52:08 +08:00 为什么不玩回调,闭包之类的 |
4 icymorn OP @tkisme2013 闭包比较容易嘛,而且在 LL 语言中我自己实现了闭包。回调就更直观了。还有好多更新奇的东西等待发掘。 |
6 zhy0216 2015-10-30 12:38:39 +08:00 由于 ll 语言在调用函数时是 call-by-value 的,所以不能用 y-combinator . Scheme 就是 call-by-value 的, 可以用 Y 啊... 这句话没读懂 |
8 icymorn OP @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 而已。 |
9 zado 2015-10-30 13:34:31 +08:00 楼主一定是 Javascript 高手,我做了一个在线执行 js 的东东,我希望把它做得功能完善一点,可是 js 语言我学得不多,你的代码能不能放在上面执行,如果不能是为什么? http://zxxq.sinaapp.com/zxjb.html 能不能帮我看一下? |
10 icymorn OP @zado 应该不能的,像 require 和 exports 我都是有重新定义的,才能使我的代码在浏览器和 node.js 中可以用同一份,代码间的相互依赖要改起来也是挺麻烦的。 |
11 amaranthf 2015-10-30 18:53:20 +08:00 首先人家 lisp 不是函数式语言……另外,括号多么优美! |
14 Kabie 2015-10-31 18:25:51 +08:00 …………赶紧看看。。。 |