Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question about clojure namespaces and macros

Tags:

macros

clojure

Suppose I've got a bunch of namespaces (apple, banana, orange). In these namespaces I use the eat macro, which calls (not "generates", calls) the peel function. The peel function is different for each fruit, but the macros are identical, and fairly big, so I'd like to create a fruit namespace that contains the eat macro. But when I call the eat macro from the apple namespace, the eat macro should call the apple/peel function.

To illustrate (but this doesn't work):

(ns fruit)
(defmacro eat [] (peel))

(ns apple)
(defn peel [] (prn "peeled apple"))
(fruit/eat)

(ns banana)
(defn peel [] (prn "peeled banana"))
(fruit/eat)

To emphasize, this means that the peel function should be called when, and only when, the macro is expanded, as in this example.

(ns apple)
(defn peel [] (prn "peeled apple"))
(defmacro eat [] (peel))
(macroexpand-1 '(eat))

So, any ideas on how to combine macros and polymorphism?

like image 622
Michiel de Mare Avatar asked Feb 25 '10 17:02

Michiel de Mare


People also ask

Does Clojure have macros?

Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.

What is a namespace in Clojure?

Namespaces are mappings from simple (unqualified) symbols to Vars and/or Classes. Vars can be interned in a namespace, using def or any of its variants, in which case they have a simple symbol for a name and a reference to their containing namespace, and the namespace maps that symbol to the same var.

What is a Clojure keyword?

Here's the Clojure documentation for Keywords and Symbols. Keywords are symbolic identifiers that evaluate to themselves. They provide very fast equality tests... Symbols are identifiers that are normally used to refer to something else.


1 Answers

What you're describing is not polymorphism but what is called local capture. You want the eat macro to "capture" the local definition of peel.

This is considered bad style in most Lisps, especially Clojure, as it can lead to subtle and unpredictable bugs.

A better solution is to pass the correct peel to the eat macro when you call it:

(ns fruit)
(defmacro eat [peeler] `(~peeler))

(ns apple)
(defn peel [] (prn "Peeled an apple"))
(fruit/eat peel)

If you really want to do local capture, you can force it with a ~' (unquote-quote) in the macro:

(ns fruit)
(defmacro eat [] `(~'peel))
like image 122
Stuart Sierra Avatar answered Oct 19 '22 18:10

Stuart Sierra