why are compojure routes defined as macros?
Asked Answered
T

1

6

for example the Luminus website states that

Compojure route definitions are just functions that accept request maps and return response maps...

(GET "/" [] "Show something")
...

But compojure routes are not functions

(defmacro GET "Generate a `GET` route."
  [path args & body]
  (compile-route :get path args body))

One can use the function make-route that returns a functions, but does not allow for destructuring. So as a function you can not use compojure's special syntax for destructing (i.e the vector) but does this stop any form of destructuring? Does the macro from given them a performance increase?

(make-route :get "/some_path" some_handler)

Couldn't the destructing syntax be passed to the function using a macro wrapper?

Testimonial answered 21/6, 2017 at 18:48 Comment(0)
A
8

One reason macros are used is so the user can write function and symbol names without having to quote everything. Take this example from the Clojure Cookbook:

; Routing
(defroutes main-routes
  (GET "/"    [] (index))
  (GET "/en/" [] (index))
  (GET "/fr/" [] (index-fr))
  (GET "/:greeting/" [greeting] (view greeting)))

All of the index* symbols, plus view and greeting would have to be quoted if GET were a function:

; Routing
(defroutes main-routes
  (GET "/"    [] '(index))
  (GET "/en/" [] '(index))
  (GET "/fr/" [] '(index-fr))
  (GET "/:greeting/" '[greeting] '(view greeting)))

Since function arguments are evaluated before the function is invoked, (index) et al would be evaluated as soon as the form was read. Also, the greeting arg will be changing on each HTTP request, so it obviously is not known ahead of time.

The macro also takes care of all the destructuring magic which is not (normally) possible with a regular function.

The thing which is often confusing (and is not well explained to beginners) is that a line like:

(GET "/:greeting/" [greeting] (view greeting))

is not normal "Clojure code". Instead, it is a type of shorthand (Domain-Specific Language or DSL to be precise) that the GET macro will ingest and use as instructions on how to generate "legal" Clojure code. The DSL is normally much shorter, simpler, & more convenient for a human than the final generated code, just as Clojure is much shorter, simpler, & more convenient than the Java Bytecode produced by the Clojure compiler, or the machine assembly language code eventually produced by the JVM.

In short, each macro is a "pre-compiler" that turns the DSL into plain Clojure, which is then ingested by the Clojure compiler to generate Java bytecode.

Having said that, it could be re-arranged to put all of the macro magic into the defroutes macro so that the GET symbol was neither a function nor a macro, but just a type of marker like the :get keyword in the implementation. As a user, these kinds of implementation details normally don't matter much.

Update

It is best to use macros only when a function won't work or is very awkward. The deciding factor is usually if one wants to use bare (unquoted) symbols, but not evaluate them in advance. Core Clojure itself uses macros for many constructs that are "built-ins" in other languages, including defn, for, and, or, when, and others.

Also note that a macro cannot do some things a function can, such as being a parameter to a higher-order function like filter.

In summary, a function defines a behavior. A macro defines a language extension.

Albinus answered 21/6, 2017 at 19:10 Comment(3)
If GET were a function, '(view greeting) would make no sense at all. You would need to write something more like (GET "/:greeting" (fn [req] (let-request [[greeting] req] (view greeting)))), which is cumbersome but not impossible.Kine
The function approach would need to use eval for most features, which is why the macro solution is greatly preferred.Albinus
@AlanThompson - purely curiosity. I'm not a big fan of macros as I believe they often obfusticate what is really happening. Always interested when projects use them heavily (design decisions etc). Just found bidi - the data structure approach speaks to me on a spiritual level.Testimonial

© 2022 - 2024 — McMap. All rights reserved.