我发现在Clojure中,特殊表单不能作为参数传递或保存在变量中:
user=> (defn my-func
[op]
(op 1 2 3))
#'user/my-func
user=> (my-func +)
6
user=> (my-func if)
java.lang.Exception: Unable to resolve symbol: if in this context (NO_SOURCE_FILE:5)
user=> (def my-if if)
java.lang.Exception: Unable to resolve symbol: if in this context (NO_SOURCE_FILE:9)
并在Racket / Scheme中:
> (define (my-func op)
(op 1 2 3))
> (my-func +)
6
> (my-func if)
if: bad syntax in: if
> (define my-if if)
*if: bad syntax in: if
这很酷,我很好,我知道我可以编写一个函数来包装一个特殊的表单,然后传递该函数。
但是我想知道为什么Lisps有这个限制,以及允许这样做的负面后果。他们是否允许这样做的主要方言?
答案 0 :(得分:4)
特殊形式不是函数:函数将值作为参数,而特殊形式采用形式。例如,请查看if
的示例:
(if 1 2 3)
好的,这很简单,因为2和3已经是值。但是这个呢?
(define (modadd a b n)
(if (zero? n) #f (modulo (+ a b) n)))
在这种情况下,if
实际上是以表单形式接收#f
和(modulo (+ a b) n)
,而不是作为值。这很重要!如果(modulo x n)
为0,则n
将失败:这就是为什么在我们知道n
不为0之前它未被评估的原因。
能够将特殊表单作为第一类对象传递的麻烦是高阶函数不能使用可预测的语义来调用这些对象:它是一个函数对象,所以你传入的值,或者它是一个特殊形式,以便你传递形式?这可能是一个巨大的混乱。
是的,您可以编写一个封装modadd
的包装函数,就像我的if
一样。但是,你无法将if
重新实现为一个函数,同时仍然保留了仅有一个评估一个分支的行为。
答案 1 :(得分:4)
它使评估更加复杂和编译困难。
如果您的表单为(a b c)
,则需要在运行时解析a
的值,然后以某种方式将其应用于表单b
和c
。< / p>
一些有限数量的特殊形式的简单模型以及其他严格的评估随后就消失了。
另请参阅:FEXPR