这是我最近使用 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
…………赶紧看看。。。
|