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 "macr开发者_运维技巧os" 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?
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.
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.
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.
精彩评论