dilvan
dilvan

Reputation: 2239

How do I write DSLs in Clojure(Script) without parenthesis?

As Clojure(Script) is a Lisp, all examples of DSLs (Domain Specific Languages) in it use parenthesis to encode were expressions begin and end (as Lisp does). However, at least another Lisp dialect (Racket), has many examples of "parenthesis less" DSLs (Creating Languages in Racket, DSL1, DSL2). I can use some ad hoc method to convert:

if (a > 10) { print "Ok!" }    ==>    (if (> a 10) (print "Ok!"))

I would like to know if there is some best practice guide or library to do these transformations in Clojure. Or maybe someone has ported Racket's functions that do these transformations to Clojure (I am not a Racket user). Can a Racket user point me to some Racket documentation about how it processes such DSLs?

Upvotes: 4

Views: 715

Answers (2)

Daniel Compton
Daniel Compton

Reputation: 14549

There is a library called chiara which lets you write Clojure without using parentheses. From the README:

(ns my-ns
  (use chiara))

(use-chiara) (chiara

defn foo [x]
  map inc (range x)

def my-list
  foo 10

)

It introduces syntax macros which let you do the kind of transformations you're after.

Upvotes: 3

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

Clojure intentionally did not add user-definable reader-macros. Which are a feature that is used to do top level code transformations. Clojure does include these, they are just not user-definable so there are a fixed number of them.

One example is the #_ reader macro that removes the next expression entirely. (you can stack them)

#_#_#_(:println  1) (println 2) (println 3) (println 4)
4

What you can do is define tagged literals which solves many of the same problems though they are not really intended for defining external DSLs. Most of the time you would have a top level dsl-start macro and then you don't have to use any ()s beyond the initial two surrounding the start symbol. With the limitation that what you present in your language must consist of readable expressions (symbolc, maps, vectors, lists, etc.)

(with-my-special-language
   if (a > 10) { print "Ok!" }
   if (a > 42) { print "Even Better!" }
)

If you really want to avoid exposing the two () to the person writing this code you can read that code into a string, add the () your self and then evaluate it. It's very handy having direct access to the reader like this.

Upvotes: 6

Related Questions