在 Clojure 的宏中声明设计模式
Lisp 的优点之一是宏。我读过很多关于在 Java 中一次又一次编写设计模式的文章。不在 Lisp/Clojure 中。
在 Lisp/Clojure 中,您可以在宏中声明该模式,并且只需编写实际的代码。
好吧,漂亮又花花公子,但眼见为实。
您能否向我提供(或推荐我)一个包含代码的示例(最好是 Clojure),说明如何在宏中声明设计模式?
One of the virtues of Lisp is Macros. I have been reading a lot that in Java you write design patterns again and again. Not in Lisp/Clojure.
In Lisp/Clojure you’d declare the pattern in a macro and you’d only have to write the actual code.
Ok, nice and dandy but seeing is believing.
Would you please provide me (or refer me) an example with code -preferably Clojure- about how to declare a design pattern in a Macro?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
大多数现有的设计模式都起源于面向对象的世界,并且仅在面向对象的世界中才有意义。一旦您进入函数式编程,尤其是像 Clojure 这样的 Lisp 方言,您对设计模式的需求就会变得越来越小。 此处有关于设计模式和 FP 的有趣讨论。
另一方面,宏并不是为了封装设计模式,而是通过更方便地解决当前问题的结构来扩展语言。采取
with-open
宏:将其称为对资源调用
close
的设计模式似乎完全错误。模式也存在于 FP 世界中,但由于不再有对象,因此它们的主要焦点是算法。 FP 语言的“模式”的好例子是 monad 和 拉链。
警告:理解这些概念可能需要一些时间,但绝对值得理解它们的每一个部分。
Most of the existing design patterns originated and makes sense only in the Object Oriented world. As soon as you step into functional programming, and maybe especially Lisp dialects such as Clojure, your need for design patters gets smaller and smaller. There's an intresting discussion about design patterns and FP here.
On the other hand, macros are not intended to encapsulate design patterns, but rather extend the language with constructs that are more handy to solve the problem at hand. Take the
with-open
macro: calling it a design pattern to invokeclose
on a resource seems simply wrong.Patterns exist in the FP world as well, but as you don't have objects anymore their main focus is on algorithms. Good examples of "patterns" for FP languages are monads and zippers.
Warning: it might take time to grok those concepts, but it's definitely worth to understand every bit of them.
实现设计模式的典型宏用法示例是应用于现有函数的“装饰器”模式。
注意:您实际上并不需要宏来执行此操作,您也可以使用高阶函数来执行此操作。
Example of a typical macro usage to implement a design pattern would be the "Decorator" pattern applied to an existing function.
Note: you don't really need a macro to do this, you could also do it with a higher order function.
它不是关于宏,而是关于函数式编程。宏可以让东西看起来更好,感觉更好,但一切都与功能有关。如果你用 FP 编写,你不会遇到很多必须一直重复的习惯用法(至少没有一个是你不能用更多的函数做得更好的)。
采用策略模式。如果您的语言有 lambda,您就不再需要它了。
通常,您可能会使用相同的模式,但您的语言使表达它变得如此容易,以至于您永远不会将其称为“实现设计模式”。这只是编程。
Its not about macros, its about functional programming. Macros can make stuff look and feel better, but it's all about the functions. If you write in FP you won't encounter many idioms that you have to repeat all the time (at least none that you can't make better with more functions)
Take the strategy-pattern. If your language has lambdas you don't need it any more.
Often you may be using the same pattern but your language makes it so easy to express it that you would never call it Implementing a Design Pattern. It's just Programming.
到目前为止所有好的答案。我想强调的是,宏和函数之间的关键实际区别在于,宏可以控制何时以及是否评估其参数,而函数则不能。除其他含义外,这允许您用该语言编写新的控制结构,而这是函数无法做到的。
例如,查看 if-not来自 Clojure 核心的宏。这与标准的“if”完全相同,但相反:如果条件为假,则运行第一位代码;如果条件为假,则运行第一位代码。如果条件为真,则运行第二个。
如果你写了类似函数的东西,那是行不通的。函数版本将在调用函数时立即运行“then”和“else”代码,无论条件为真还是假。另一方面,宏版本可以选择是否(以及何时)运行作为参数给出的代码。
一个不太简单的例子是 抽象出典型的“尝试” ... catch ... finally”控制结构在 Java 中无处不在,在其他语言中也很常见,变成了“with-whatever”宏。如果您持有有限的资源,例如必须释放的文件句柄或网络套接字,即使出现错误,您也需要一个“finally”块来执行此操作,但您的应用程序代码必须未经评估地拼接到“try”块的内部,在该上下文中运行。
您最终会在程序中的任何地方复制本质上相同的未更改的样板 try...catch...finally 块,并将一个小的本地适用部分粘贴到“try”部分中。 (查看任何重要的 Java 源代码。)此样板文件无法抽象为函数,因为函数在调用时会在调用者的上下文中立即计算本地代码,然后给出结果 /em> 该代码到“with-whatever”函数。
另一方面,宏可以延迟求值,直到宏代码特别要求为止,从而允许它将任意本地代码拼接到未求值的“try”构造的内部。然后,整个结果(整个 try/catch/finally,包括 try 中未评估的应用程序特定代码)返回给调用者并拼接到调用者的上下文中,完成,以便在那里执行。
因此,您可以编写一个可以编写程序的程序。
All good answers so far. I would like to amplify that the key practical difference between a macro and a function is that a macro can control when and if its arguments are evaluated, and a function cannot. Among other implications, this allows you to write new control constructs in the language, which you can't do with functions.
For example, take a look at the if-not macro from Clojure core. This is exactly like the standard "if" but inverted: if the condition is false, it runs the first bit of code; if the condition is true, it runs the second.
If you write something similar as a function, it won't work. The function version will run both the "then" and the "else" code immediately when the function is called, regardless of whether the condition is true or false. The macro version, on the other hand, can choose whether (and when) to run the code it's given as arguments.
A less trivial example is abstracting out the typical "try ... catch ... finally" control construct ubiquitous in Java and common in other languages into a "with-whatever" macro. If you're holding a finite resource such as a file handle or a network socket that must be released, even in case of error, you need a "finally" block to do that, but your application code has to be spliced unevaluated into the interior of the "try" block, to be run within that context.
You wind up copying essentially the same unaltered boilerplate try...catch...finally block around everywhere in your program and pasting a small locally applicable section into the "try" part. (Look at any non-trivial Java source code.) This boilerplate can't be abstracted into a function, because a function would evaluate the local code immediately when it's called, in the caller's context, and then give the result of that code to the "with-whatever" function.
A macro, on the other hand, can delay evaluation until specifically called for by the macro's code, allowing it to splice your arbitrary local code into the interior of the "try" construct unevaluated. The entire result (the entire try/catch/finally, including your unevaluated app-specific code within the try) is then returned to the caller and spliced into the caller's context, complete, to be executed there.
You can thus write a program that writes programs.