Clojure 中的 DSL 取代了面向对象的软件解决方案?

发布于 2024-12-10 02:41:49 字数 326 浏览 0 评论 0原文

我想知道是否有人知道 Clojure 中 DSL 的具体示例,它取代了良好的 OO 程序(用 Java 编写)的抽象和可读性。

我一直在尝试将 OO 数据模型(基于“bean”,具有隐藏底层实现的非常抽象的方法)引入到 clojure 部分中。

我知道“宏”和“高阶函数”的存在,但是,我从未见过它们应用于易于理解的现实世界数据集(例如课程注册系统、汽车经销商或计费系统)系统或类似的东西,回想一下 Hibernate 和 Ibatis 在过去十年中普及的臭名昭著的“JPetStore”示例)。

是否存在特定于领域的模板来学习如何使用协议和高阶函数在 Clojure 中对现实世界系统进行建模?

I was wondering wether anyone knew of a concrete example of a DSL in Clojure which replaces the abstraction and readability of a good OO program (written in, say , Java).

I've been trying to take an OO data model (which is "bean" based, with very abstract methods which hide underlying implementations) into a clojure moeity.

I know that "macros" and "higher order functions" exist , however, I've never seen them applied to a real world data set which is easily understood (for example a course-registration system, or a car dealership, or a billing system, or something of the sort, recall the infamous "JPetStore" examples which Hibernate and Ibatis popularized in the last decade).

Does any domain-specific template exist for learning how to model real world systems in Clojure with protocols and higher-order functions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清风不识月 2024-12-17 02:41:49

DSL 没有特殊的模板 - 您只需使用语言中可用的工具并尝试使其尽可能方便且接近领域。 Lisp 只是为您提供了比其他语言更多的工具。

有关良好 DSL 的具体示例,请查看 ClojureQL。最初,SQL 是作为关系数据库的 DSL 创建的。从控制台工作非常方便……但不是从 Java 或 Clojure 等编程语言工作。 Java 附带了 Hibernate 等大型 ORM 框架,而 Clojure 提供了简单的 DSL,它与原始 SQL 一样方便,但完全作为语言的一部分工作:

(select (table :users) (where (= :id 5)))

Lisp DSL 中的常见之处是使用像 defsomething 这样的结构。例如,在一本书中(抱歉,我不记得它的名字)有一个文本中模式匹配的示例。作者创建了包含许多匹配器的模块,例如 ? 用于一个单词,+ 用于一个或多个单词,* 用于零个或多个单词等等在。为此,他创建了宏 defmatcher,该宏采用一些语法并将该语法的处理程序添加到中央注册表。这只是抽象 - 他没有引入多个重复操作,而是引入了单个宏来告诉他实际想要做什么 - 定义匹配器。此示例还同时使用宏和高阶函数。

因此,再一次强调,基于 Lisp 的 DSL 没有什么特别之处 - 您只需使用您语言中拥有的工具来描述领域区域,无论是 Java、Clojure 还是其他语言。只需使用语言设施,您就会看到它的样子。

UPD。一些“现实世界”的例子,其中基于 Lisp 的 DSL 比 OOP 更方便:

域:汽车迪勒船

(defcar my-cool-car :wheels 4, :doors 2, :color red) ;; in Java you need Factory
(def car1 (make-car my-cool-car))                    ;; and lots of methods to 
                                                     ;; add features to cars and 
                                                     ;; cars to factory

域:计费系统

(transaction                ;; in Java you cannot create wrapping constructs
  (withdraw account1 100)   ;; so you have to use inheritance, annotations, etc.
  (put account2 100))       ;; which is much more code

域:一些 Web 服务,处理多种类型的请求

(defhandler :show-all (fn [params] ...))     ;; adds defined function to the
(defhandler :find-best (fn [params] ...))    ;; map of :message-type -> function
...
(defn handle [message]
   (let [msg-type (:type message), msg-params (:params message)]
     (if (contains? *handlers* msg-type) 
       ((*handlers* msg-type) msg-params)
       (throw (Exception. (concat "No handler for type" (:type message)))))))

这些示例没有什么特别之处 - 您可以用 Java 或任何其他语言来实现它们。不过,诸如关键字(第一个示例)、高阶函数(第二个示例)、宏(所有 3 个示例)之类的内容使您的代码更加简洁和具有描述性。

There are no special templates for DSLs - you just take tools available in the language and try to make it as convenient and close to the domain as possible. Lisp just gives you more tools than other languages do.

For concrete example of nice DSL look at ClojureQL. Initially, SQL was created as DSL for relational databases. And it is very convenient for working from console... but not from the programming language like Java or Clojure. Java came with large ORM frameworks like Hibernate, and Clojure offers simple DSL which is as convenient as original SQL, but works completely as part of the language:

(select (table :users) (where (= :id 5)))

Common thing in Lisp DSLs is using constructs like defsomething. For example, in one book (sorry, I don't remember its name) there's an example of pattern matching in text. Author creates module with a number of matchers like ? for one word, + for one or more words, * for zero or more words and so on. For this purpose he creates macro defmatcher that takes some syntax and adds handler for this syntax to the central registry. This is just abstraction - instead of several repeated operation he introduces single macro telling what he actually wants to do - define matcher. Also this example uses both - macros and high order functions.

So, once again, there's nothing special in Lisp-based DSLs - you just describe domain area with the tools you have in your language, be it Java, Clojure or anything else. Just get used with the language facilities and you will see how it must look like.

UPD. Some "real-world" examples where Lisp-based DSLs are more convenient than, say, OOP:

Domain: car dillership

(defcar my-cool-car :wheels 4, :doors 2, :color red) ;; in Java you need Factory
(def car1 (make-car my-cool-car))                    ;; and lots of methods to 
                                                     ;; add features to cars and 
                                                     ;; cars to factory

Domain: billing system

(transaction                ;; in Java you cannot create wrapping constructs
  (withdraw account1 100)   ;; so you have to use inheritance, annotations, etc.
  (put account2 100))       ;; which is much more code

Domain: some web service, that handles requests of several types

(defhandler :show-all (fn [params] ...))     ;; adds defined function to the
(defhandler :find-best (fn [params] ...))    ;; map of :message-type -> function
...
(defn handle [message]
   (let [msg-type (:type message), msg-params (:params message)]
     (if (contains? *handlers* msg-type) 
       ((*handlers* msg-type) msg-params)
       (throw (Exception. (concat "No handler for type" (:type message)))))))

There's nothing special about these examples - you can implement them all in Java or any other language. Though, things like keywords (1st example), higher-order functions (2nd example), macros (all 3 examples) make you code more concise and descriptive.

梅窗月明清似水 2024-12-17 02:41:49

我不知道这是否是您正在寻找的内容,但我读过一本关于 Clojure 的书(Clojure 编程;实用程序员),其中包含一个不错的小 DSL 示例。您可以在 https://github.com/stuarthalloway/lancet 找到代码。基本上,lancet 类似于 make 或 ant,但作为 Clojure-DSL 实现。

I don't know if this is what you are looking for but I've read a book about Clojure (Programming Clojure; Pragmatic Programmers) which contains an example of a nice little DSL. You can find the code at https://github.com/stuarthalloway/lancet. Basically lancet is something like make or ant but implemented as a Clojure-DSL.

无敌元气妹 2024-12-17 02:41:49

OOP 是好的,因为我们确实已经习惯了它,并且它映射到现实世界中的概念。

您可以在 The Joy of Clojure 第 9 章中了解如何使用映射和多方法构建 OOP 系统(类似于 JS 中的原型继承)。Paul

Graham 的 lisp 书展示了如何在 lisp 中创建对象系统。应该很容易使其适应 Clojure。

本书还广泛地解释了“自下而上”的编程,即构建小的基本块并将它们组合起来以构建更高级别的结构。

另外,如果您可以看到许多应用程序(例如 java pet store)中的主要概念是程序性/功能性;他们不广泛使用泛化或封装等对象概念。

Bean 没有任何行为。未封装的数据具有允许公共访问的 getter/setter。对于它提供的功能,您可以使用 C 结构体(类型化语言)或动态语言(如 Clojure)中的映射。

对它们起作用的服务基本上是函数。它们没有状态并从参数(bean)或数据库中获取数据。如果您确实需要接口,Clojure 中有协议。

那没那么复杂。

像在 JAVA 中一样命名您的 bean...但将它们实现为 Clojure 映射或记录

命名您的服务并将它们实现为函数。

需要工厂吗?创建接受配置和返回函数的函数。

需要依赖注入吗?那么你可以使用函数参数。

OOP is good in the sense that we are really used to it, and it maps to concepts in the real world.

You can see how to build an OOP system with maps and multi methods (something similar to prototypal inheritance in JS) in The Joy of Clojure, chapter 9.

The lisp book from Paul Graham shows how to create an object system in lisp. Should be easy to adapt it to Clojure.

This book also explains extensively "bottom up" programming, that is building small basic bricks and composing them to construct higher level constructs.

Also if you can see the main concept in many applications (like java pet store) is procedural/functional; they don't use objects concepts like generalization or encapsulation extensively.

Bean has no behavior. Data with no encapsulation have getters/setters that allow public access to them. For what it gives you, you could use a C struct (in typed language) or a map in dynamic language like Clojure.

Services that work on them are basically functions. They have no state and take data from parameters (beans) or database. If you really need an interface, you have protocols in Clojure.

That's not that complicated.

Name your beans like in JAVA... But implement them as Clojure maps or as records

Name your services and implement them as functions.

Need factories? Create functions that take a configuration and return functions.

Need dependency injection? Well you can use function parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文