Lisp的生产性

组里以前的代码大多数是 Clojure, 现在人员变动比较大就很尴尬。唯一一个能大概看懂的就剩我一个写过简单 elisp 的

会 Lisp 的人越来越少了,项目早晚要换用能招到人的语言

估计以后像我这样的,只能根据 DEMO 做一些简单的 Emacs 设置的,是常态

现在流行的语言都有些自己的一两个特色, 要么是比较方便维护 (Java, Typescript), 要么学习成本低 (Golang, Python), 要么有性能优势(C, Rust), 要么是站上了某个风口的 (Javascript, Scala).

Common Lisp 嘛, 除了学习成本这项可能还不错, 个人感觉其他都不太行, 所以也只能没落了. 不过 Lisp 系列作为教学语言还是很不错的, 很多地方拿它教编译器, 也算是发挥下余热吧.

1 个赞

写了一大堆我又删掉了。 简单点说2个字 “难用”

1 个赞

Lisp 里面最能打的就是 Clojure 了,最近的 Roam Research, Logseq, Clubhouse 之类的都是 Clojure 的项目。但是因为意识形态的原因。。。

Clojure 这语言就是 Java 社区看了觉得全是括号,Lisp 社区看了觉得是 Java 就不好。这个问题在于 Java 社区不懂宏的表达力,Lisp 社区不懂 JVM 的生态优势。

另一类就像是 Elixir 这样,对应特定的社区来搞,对于 Ruby 社区就是很像 Rails,对于 Lisp 社区就是我有宏,对于 Erlang 社区就是也能 OTP。

Clojure 像是考虑怎么能把几个优点融合,而 Elixir 像是怎么用特定的卖点获取特定用户。

7 个赞

有点意外没人提到 Grammarly,应该是目前比较知名的一个 Common Lisp 应用

5 个赞

咦,居然是Lisp吗?天天用,但是真不知道是用Lisp写的

Common-Lisp: Success Stories | Common Lisp (lisp-lang.org)

Scheme: GNU’s programming and extension language — GNU Guile

GNU Guix System GNU的高级发行版和事务性包管理器 — GNU Guix

Nyxt Nyxt browser: The Internet on your terms (atlas.engineer)

太多了。可以看到很多软件都是用Lisp方言写的,这些软件平时你也用不到,对很多人都存在较高的使用门槛,略显复杂,Lisper却可以狂欢。世界很大,路途很远,书山有路,学海无涯。

2 个赞

LISP自己生态圈之外还有人用的,现在大概只有Emacs一个了。

工业使用需要能比较容易招到人,LISP用的人太少不能满足。

业余项目我自己试过一下,感觉SBCL的性能对于我感兴趣的音频和图形不够用。当然肯定会有人给我指出一些文章说SBCL性能可以逼近C。这些文章我都看过,理论上确实可以成立,但是这样需要我把LISP写成C的形状,那我还不如直接写C得了。

最近看到这个陈年老贴被顶上来了,我感觉论坛之前还有过类似的话题,而且还叠了不少楼来着2333

我倒是很少听到Lisp生产性(Productivity)很高的说法,我觉得可能一部分人想表达的是语言的表现力(Expressive Power)

如何直观的理解表现力,有一个著名的Expression Problem,但是如今大部分人的讨论前提都是在有类型系统的语言上,因为动态语言很容易就解决这个问题(ruby的open class和common lisp的multiple dispatch)。粗暴点来说,如果不关心类型,这些都可以通过macro来模拟,比如见 Racket用macro模拟multi-method,但是没有约束,不够safe,这篇文章讨论的挺清楚。

我认为广义上生产性指的是:除了要有一定的语言表现力(做出可拓展性的数据抽象和方法抽象),还要有一定的性能保证和行为约束。

lisp在性能上的话,不太熟悉sbcl(据说是工业级别的编译器),但是chez scheme因为能够生成高性能的机器码已经成为学术界很多人写编译器的主要选项之一了,包括最近很潮流的的idris2

行为约束就是排除掉傻瓜错误,提供好的封装等等,让多人合作更加高效率,编译期间一般通过类型系统来保证,运行期则是通过契约contract来排除掉一些错误行为,类型系统Lisp/Scheme没有(除了Typed Racket),contract的话在Racket和Clojure(?)有成熟的实现。

5 个赞

语言的表现力,感觉终点就是 DSL,借助 macro,Lisp 可以比较方便的“造”一个 DSL 出来,一个实际的例子是

很好的感觉,事实上Expression Problem被提出的时候,就是写解释器(interpreter)时候发现的问题。

假设我要实现一个计算器解释器,也可以理解为DSL,那么如何在不修改原有代码的前提下,添加新的数据类型与操作,这就是最经典的例子。

用macro写DSL是racket一直在喊的口号,详情见Racket Manifesto

可惜在实践过程中,我并没有得到方便的感觉,也与几位Racket爱好者讨论过此类问题。
当然我不否认如果DSL的形式是s-expression,macro有着绝对的优越性

你用 macro 去表达一个东西,仅仅是能写,不会变方便。比方说你用 macro 去实现个写 SQL 的 DSL。不管设计多好,你本质上还在写 SQL。所以想要方便的话,Data > Function > Macro 越靠前的话越容易利用现有的函数来组合,如果你用一个数据结构表达 SQL,那就可以用现有的函数来组装这个结构,获得了运行时的灵活性,提高了表达力。

Macro 最大的好处是减少样版代码,让语言更简洁,从这样的角度提高是生产力。这两个并不是同一个方向。

当你觉得因为写个 DSL 更简单更高效的时候,实际上是以“把主观加入到下层基础”为代价的。

4 个赞

谢谢狗哥的解释! 把我表达不出来的感觉说出来了,我直觉与经验上的macro优势确实还是meta-programming相关,是在编译期增加语言自身的拓展性。

竟然没人提到 grammerly 耶

学姐肯定是没看到

哈哈哈哈哈哈哈

不仅仅是能写,最主要是其抽象能力,省去了实现细节。小的例子是 when-let ,大的例子是 use-package,在一个大型软件里面,这层抽象尤为必要。

函数也能起到抽象的作用,与 macro 什么区分呢?

macro 是编译时期执行的函数

说白了,macro 也是函数,只是执行阶段不一样。

总结的不错,我这里补充下原因。因为上面说了,macro 本质也是函数。之所以 function 比 macro 更容易利用,是因为现有函数都是为操作具体业务数据而写的,而 macro 这类的函数,是以 s-exp 为主要操作目标。

这句话感觉也能再延伸下。Macro 不仅仅是让语言更简洁,而是创造一个“新”语言。举一个 cascalog 的例子:

(?- (stdout)
    (<- [?word ?count]
        (sentence :> ?line)
        (tokenise :< ?line :> ?word)
        (c/count :> ?count)))

上面代码片段是大数据领域一个经典示例 word count(类似于 hello world),cascalog 虽然是用 Clojure 写的,但是用法完全是个“新”语言,熟悉了这个“新”语言可以很方便的进行 map reduce 程序编写。

2 个赞

这样似乎和 web 前端造的各种工具有点类似, 比如 purescript, typescript 和 babel, 思路上都是把不同的语言转换成 javascript. Racket 这边的话, 看起来似乎是想用 macro 代替那些编译器实现这些转换功能.

感觉确实会有些问题, 一个是 Racket 语法本身的灵活性能不能很好兼容各种语法, s-expression 虽然已经比较灵活, 但是很难涵盖到各种方面. 另一个是如果想要实现比较复杂的语义, 虽然理论上可行, macro 本身的复杂度和性能就挺难控制的. 综合下来, 除了小规模并且比较简单的 DSL, 很可能不如专门的编译工具好用.