这里我的重点是Scala和Lisp / Scheme宏,不完全是C / C ++ / Obj-C中的那些
我只是不明白这一点。
我理解它的方式是,宏可以扩展语言。但功能也是如此。
据我所知,由于某些语言限制,有些事情无法实现,因此需要采用这种方式。但是我用宏看到的很多例子似乎都是使用普通函数实现起来相当简单的事情。
那究竟是什么目的呢?清洁宏或其他,有人请赐教。如果可能的话,请提供一些可以在宏中完成的示例代码,但是对于普通函数来说是不可能/很难的。
答案 0 :(得分:7)
简要总结了Scala宏已完成的工作:http://scalamacros.org/paperstalks/2014-02-04-WhatAreMacrosGoodFor.pdf。总而言之,已知宏有利于:1)代码生成,2)高级静态检查,3)授权特定于域的语言。让Scala中的宏基于类型为像Lisp类语言中的宏中常见的功能提供了额外的强大功能。
上面链接的幻灯片中的一些示例确实可以在没有宏的情况下实现,但结果将在某种意义上缺乏(例如,在性能方面)或者对于用户来说过于复杂(例如,由于重量级错误消息)。例如,Akka的类型化通道可以用纯粹的暗示实现,但编译速度和可理解性会受到影响。或者,scala / async可以作为编译器插件实现,但是它必须依赖于内部编译器API并且更难分发。
当然,宏不是银弹。当它们不是最佳选择时,显然存在用例,这是http://scalamacros.org/paperstalks/2014-03-01-MacrosVsTypes.pdf中概述的内容。然而,令人好奇的是,在许多情况下,无论是纯宏观还是无宏观解决方案,而是精心构建的混合动力最终都是最好的。
答案 1 :(得分:2)
在Common Lisp和Scheme中,大多数特殊语法确实是根据其他特殊语法实现的,即宏。
例如,Scheme和CL都有if
,cond
和case
,但只有if
是原始语法。
标准定义的宏和您自己制作的宏没有什么特别之处。它们可以表现得像原始一样好。
Macros会让读者感到迷茫和惊讶。使用像with-*
这样的常见命名约定可能会有所帮助,但如果函数/过程可以完成这项工作,或者表单只在少数几个地方使用,那么就不应该使用宏。
答案 2 :(得分:1)
在Lisp中使用宏有三个主要目的(不了解Scala):
defun
,defgeneric
,defclass
(来自标准),deftable
(来自后现代)。Unwind-protect
包装器:暂时修改状态并确保在完成任务后修改状态。这可能很麻烦,反复写,所以我们创建一个速记。示例:with-open-file
(标准),with-transaction
(许多数据库库)。具体示例:Java 7引入了一种速记,用于确保在Closable
中关闭try
- 块:
try (SomeClosable foo = openFoo()) {
foo.doSomething();
}
只能在Java 6中大致表达如下:
SomeClosable foo;
try {
foo = openFoo();
foo.doSomething();
} finally {
if (foo != null && foo.isOpen()) {
foo.close();
}
}
Java开发人员必须等待语言设计人员实现此功能。 Lisp开发人员使用一个小宏:
(defmacro with-open-foo ((var &rest options) &body body)
`(let ((,var (open-foo ,@options)))
(unwind-protect
(progn ,@body)
(when ,var (close ,var)))))
这样他就可以写了
(with-open-foo (f :bar baz)
(do-some-foo f)
(and-something-else))
而不是
(let ((f (open-foo :bar baz)))
(unwind-protect
(progn
(do-some-foo f)
(and-something-else))
(when f (close f))))