Common Lisp中的Haskell风格部分

jay*_*ica 9 lambda haskell common-lisp

在Haskell中,如果我有一个如下所示的lambda

(\x -> doStuff x y)
Run Code Online (Sandbox Code Playgroud)

这里y是从周围的范围,我可以部分它,把它变成

(`doStuff` y)
Run Code Online (Sandbox Code Playgroud)

这是更短更简洁的(也是我最喜欢的Haskell之一).

现在,在Common Lisp中,我会将等效代码编写为

(lambda (x) (do-stuff x y))
Run Code Online (Sandbox Code Playgroud)

这对我来说实际上是一件很常见的事情,但是我觉得即使是那么一点点样板也让我感到困扰,所以我想知道是否有办法在Common Lisp中获得类似Haskell风格的部分?

Rai*_*wig 11

除非你更有经验,否则我建议你学习在Lisp中编写Lisp,而不是在Lisp中编写Haskell.后者不是一个好主意.Haskell的工作方式非常不同.

Lisp没有做任何'currying'(或schönfinkeling;-)).

你可以把它写成:

CL-USER 5 > (defun curry (fn arg) (lambda (&rest args) (apply fn arg args))) 
CURRY

CL-USER 6 > (mapcar (curry #'expt 2) '(2 3 4 5 6))
(4 8 16 32 64)
Run Code Online (Sandbox Code Playgroud)

不过,它的成本有点高.

CL-USER 7 > (mapcar (lambda (base) (expt base 2)) '(2 3 4 5 6))
(4 8 16 32 64)
Run Code Online (Sandbox Code Playgroud)

我个人更喜欢后者,因为我有一个真正可读的变量名称.这有助于调试器,然后我看到一个回溯.像这样的工具在Lisp中可能比在Haskell中更重要.

CL-USER 12 > (mapcar (lambda (base) (expt base 2)) '(2 3 "four" 5 6))
Run Code Online (Sandbox Code Playgroud)

错误.我们来看看回溯:

CL-USER 12 : 1 > :bb
...

Condition: In EXPT of ("four" 2) arguments should be of type NUMBER.

Call to SYSTEM::ARGS-TO-BINARY-ARITHMETIC-FN-NOT-OF-TYPE {offset 189}
  SYSTEM::FN-NAME : EXPT
  SYSTEM::ARG1    : "four"
  SYSTEM::ARG2    : 2
  TYPE  {Closing} : NUMBER

Interpreted call to (SUBFUNCTION :ANONYMOUS SYSTEM::ANONYMOUS-LAMBDA):
  BASE : "four"
Run Code Online (Sandbox Code Playgroud)

现在我可以看到这个东西有一个名字.我正在"four"使用名为的变量将字符串传递给函数base.

使用REPL和调试工具进行交互式开发很常见.最好准备代码对这种开发风格有用.Common Lisp没有经过优化,无法为完整的程序编译器提供广泛的类型检查 - 就像在Haskell中一样.

Lisp的一个主要问题是很难找到代码片段的真正功能.默认(具有前缀语法的严格功能程序)相对容易理解.但是有很多可能性改变Lisp中的代码含义(宏,读取宏,符号宏,元对象协议,建议......).

第一条规则:如果您正在编写基本的Lisp代码,请坚持基本的句法和语义可能性.写防御性的.期望别人需要了解代码.为此,代码应该是可读的,易于理解的,使用常见的习语,它应该是可调试的.

在Haskell中,许多具有数学背景的人希望以非常紧凑的方式编写具有高抽象级别的代码.你也可以在Lisp中做到这一点.但是对于普通代码,我不会去那条路线,对于更大的代码片段,Lisp经常使用其他机制(通过宏进行代码转换,...​​...).

  • 三件事(其中两件是挑剔).首先,你的'咖喱'不能解决问题; 所描述的不只是currying,而是一个句法特征(将函数应用于第二个参数).其次,我的大部分Haskell开发都是在REPL上进行的,我认为这是典型的; 另一方面,虽然GHCi(标准编译器的REPL)有一个内置的调试器,但我几乎没有使用它,我认为这也是典型的(但不那么).第三,我不认为"数学背景"与你是否发现`\ x - > doStuff xy`,`flip doStuff y`或`(\`doStuff \`y)`更具可读性有关. (3认同)

Vse*_*kin 9

您可以为此类表单开发任意特殊语法.有多种变体.例如,我使用Clojure启发的尖锐反引语法.使用它,您的表单将如下所示:

#`(do-stuff % y)
Run Code Online (Sandbox Code Playgroud)


Vat*_*ine 5

我认为你不能直接做到,但......

如果你知道你总是想做一些与之相当的东西(lambda (x) (fun x lexical))并且只是想要一种较短的表达方式,那么理论上你可以使用一个宏.

我个人会建议不这样做,(lambda (x) (fun x lex))不需要打字太多,并从代码中删除一层默默无闻.但如果它是一种足够常见的模式,它需要特殊处理,那么类似下面的内容可能会:

(defmacro section (function lexical)
   (let ((sym (gensym))
     `(lambda (,sym) (,function ,sym ,lexical))))
Run Code Online (Sandbox Code Playgroud)

这使得Haskell部分:

(`doStuff` y)
Run Code Online (Sandbox Code Playgroud)

成为Common Lisp部分:

(section dostuff y)
Run Code Online (Sandbox Code Playgroud)

我不这样认为它更具可读性,至少在短期内如此,但如果它是我一次又一次看到的东西,我确实会考虑(并且已经做过,更多用于实验目的而不是其他任何东西)a宏,使它更快(我有一个半生不熟的宏,在某个地方,允许你做的事情,如(_ func _2 lexical _1)- > *(lambda (a b) (func b lexical a))这有时很方便,但并没有真正提高可读性).