这才是我苦苦追寻的lisp呀!
生物学上,我们知道基因有隐性和显性。如果爸爸的眼睛是蓝色,妈妈的眼睛是棕色,那么他应该继承了来自父母的基因:即有蓝色又有棕色的。而表象可能他的眼睛是棕色的。
对于语言也是一样,我们要区分真正的基因...和表象。我们强调的是基因而不是表象。
lisp的基因:递归作为表达过程调用的主要方式
使用链表表示复杂数据结构
通过定义函数来编程
垃圾回收
代码即数据
事实上Python,Ruby,Clojure甚至TCL/tk都带有lisp的基因。
:::::::::::::::::::::::::::::::::::::::::: 先怼传统老掉牙的lisp :::::::::::::::::::::::::::::::::::::
common lisp,曾经是最正统的lisp。但是25年之后,它不再是lisp基因的正确表象。
为什么?它犯了一些错误:
首先,它太臃肿了。
但是它又确缺少一些重要的东西。FFI,线程和并发(它发明的年代还没有这些东西)。
库。混乱,拙劣,并且低质量的库(形容Python亦可,嗯,放个嘲讽)。
没有模式匹配和静态类型。非常过程式。
委员会的那一群傻逼既肤浅又保守,不思进取。新手们都受不了那一堆括号,他们却耸耸肩:“你不懂lisp”。
那一堆的括号根本就不是基因,CL还在强调那些老掉牙的表象,而缺失了真正的lisp基因。
反倒是python,在lisp基因上面套了一层语法糖,这门语言就流行了。
在shen之前我最喜欢的lisp方言是scheme。它的哲学是:“计算机语言不应该进行功能的堆砌,而应该尽可能减少弱点和限制,使剩下的功能显得必要”。这一点我不能同意得更多。然而受限于lisp的表象,它走得还不够远。
shen显然在common lisp里面吸取教训:不要沉迷于表象。表达真正的基因。
:::::::::::::::::::::::::::::::::::::::::: 再怼那些欺负lisp没有类型的傻逼语言 ::::::::::::::::::::::::
这是一门足够先进的lisp方言。
shen回归到了简单,它的核心定义了一个非常小的lisp,比scheme规模还要小得多,在它那边叫kernal lambda。内嵌了yacc和prolog,语法糖和类型都编译到kernal lambda。
shen保留了lisp基因,并且从ML,甚至prolog那边拿到了一些真正好的东西。比如基于模式匹配的函数式编程,类型系统和安全性。
据说它的类型系统的实现只用了511行的代码。怎么做到的?用这些代码它会生成大约200多条规则和公理,然后将这些规则变成大约27,000行的Common Lisp代码!天呐,这么神奇?因为lisp的代码即数据呀。
为了说明它的先进性,让我们看一个例子。如果我要定义一个集合类型set,该怎么办?集合是这样的,它里面没有重复的元素。
(datatype set
____________
[] : (set A);
if (not (element? X Y))
X : A; Y : (set A);
================
[X | Y] : (set A);)
这段代码是说,空[]算是一个集合;
如果Y是一个集合,如果X不是Y里面的元素,那么,`[X | Y]`也是一个集合。
[1 2 3] : (set number)
["sasdf" "bc"] : (set string)
[1 1 2] : type error
["aa" 1] : type error
牛逼不!这是实现了dependent type类型啊!请我们有本事的ML/Haskell语言也定义一个set类型试试?
Haskell,请开始你的表演(此处应有掌声)。
参考资料:
回复评论。
知乎的评论系统小抽风了,那边回复不了。 @nameoverflow
set类型的例子其实是一个简化的版本,是为了方便不了解shen的人理解。
实际上这样定义set之后,下面这个函数根本没法编译:
(define union
{(set A) --> (set A) --> (set A)}
[] S -> S
[X | Y] S -> [X | (union Y S)] where (not (element? X (union Y S)))
[_ | Y] S -> (union Y S))
为了让union能编译,需要修改下set的类型定义:
(datatype set
_____________
[] : (set A);
X : A; Y : ((set A) & (without X));
===================================
[X | Y] : (mode (set A) -);
X : (set A);
_____________
X : (list A);
X : P; X : Q;
====================
X : (mode (P & Q) -);
________________
[] : (without X);
if (not (= X Z))
Y : (without Z);
______________________
[X | Y] : (without Z);
______________________________________________
(not (element? X Y)) : verified >> Y : (without X);)
其中最最最重要的就是verified这个关键字,它可以解答你的疑问。
[X | Y] S -> [X | (union Y S)] where (not (element? X (union Y S)))
这里面有个where条件,就是说,只有满足条件才走这个句子,而对应的在类型定义里,它就是verified。
也就是它并不是在运行期去check条件满足,而是编译期能知道条件满足。你可以当它是个trick。不得不承认,在理解shen的类型系统的实现之前,使用起来还是比较不那么方便的。
再补充,实名反对 @开源哥 的部分观点
Shen 的类型系统是基于 Sequent Calculus 做的,定义类型的方法是手写推理规则
这就是我说的语法不够 Lisp 的地方(之一)
你怎么能这么说呢?宏和DSL本来就是lisp的精华之一! Shen 在这里恰巧把 Lisp 的精华表达到了极致!
不是 Shen 的语法不够 Lisp,而是你没领会 Lisp 语法的真正魅力呀,同学!
那么问题来了,Lisp 的特征之一,quote 呢?
抱歉,不存在的
1. quote到底好不好?
纵观函数式语言的发展史,语法从 ISWIM 那个地方就开始分叉:Lisp 和 Haskell。Lisp 从发明之后 sexp 就没有真正变过(本来也没有真正意义上的语法)。而另一支 KRC & Miranda,ML 到 Haskell 这边都是受到 ISWIM 影响的,你可以看一下《the next 700 Programming Languages》。
看起来你受其它语法影响比较重。quote 让“符号”和“语法”之间成为模糊的存在,但我不认为它是 Lisp 精华的东西,它并不“所见即所得”。比如,像这种东西真的好读么?
(cdddr`(this list contains ,(list 'a)))
2. quote 是不是必须的特征?
转念一想,quote 是不是必须的呢?答案:不是。
像 scheme 这种lisp1里面,当输入 `map` 会对 symbol 求值。所以scheme会得到map绑定的函数...也就是,它把map是当作变量解释的。于是为了得到 map 这个纯粹的符号,需要使用特殊表 quote。
而 Shen 对 symbol 的求值规则有点特殊(lisp2)。它对symbol不会直接求值,于是你输入 `map` 得到的就是 map 这个符号。
这个问题的根源是这样的:尽管 Lisp 可以做到“代码即数据”,写代码的人还是得写明白,他想把一个东西当代码使还是当数据解释。
导致的结果是:在 scheme 中,一个 syntax 想当 symbol 使用的时候,都要用 quote 关键字。在 Shen 里面,一个 syntax 想当变量或者函数使用的时候,都是写成 (value xxx) 或者 (function xxx)。
这很蛋疼,所以在 Shen 里面做了一个约定,大写开头全是变量,缓解这个问题。
quote 存在的真正意义就是,让用户可以选择到底是代码还是数据,但它只是实现的途径之一,但并不是必须的。
所以在 Shen 里你做不到 Lisp 的“像操作数据一样操作代码”
是不是区分圆括号和中括号之后,Shen 就不是代码即数据了?这些在进入到内部表示之前,都不算真正的 sexp。在内部只有统一的一种形式,reader 会自动处理,你可以理解成统一只剩下中括号那种表示(转化到内部的 sexp 之后外部表示是啥不重要)。
(define x ''(list a)) 变成
(define x (quote (quote (list a))))
(define x [[list a]]) 变成
[define x [cons list [cons a nil]]]
我觉得没啥区别呀,这怎么就不能像操作数据一样操作代码了?你要讲道理呀,同学!
最后,我看你签名是 Making LISP Great Again (
哥!你真的不是 Haskell 派过来 Lisp 阵营的奸细?