1

I have namespaces foo and bar. Both have function hello.

I'm trying to write a function that calls either foo/hello or bar/hello based on a parameter. In other words:

(defn say-hello [target-namespace]
  (if (= target-namespace "foo")
    (foo/hello)
    (bar/hello)

Something like that, except that imagine there are 15 namespaces, so a simple if-else wouldn't suffice. It would be better to somehow leverage target-namespace and figure out the function to call from that. Is there a good way to do this?

5
  • What sort of functions do you have spread across different namespaces? Just because it seems quite an unusual thing to do. Have you tried looking into some of the built-in options for polymorphism in Clojure, like multi-methods or protocols/records? Multi-methods, for instance, would allow you to have (hello) call a different function, depending on any function of the arguments. Commented Apr 5, 2014 at 17:41
  • Really this question is under the auspices of ClojureScript. I have a Java background, so polymorphism would be great... I'm trying to do something like an interface or abstract class. That said, I'm not sure if ClojureScript, which translates to Javascript, can do it. Commented Apr 5, 2014 at 17:45
  • 2
    Ah, got you. Yep ClojureScript provides almost all of the feature of Clojure, including multimethods and protocols. Protocols are probably your best bet - they are a bit like an interface. Commented Apr 5, 2014 at 17:48
  • Looks like it should work. I'm going through this link right now to try and wrap my head around it: blog.8thlight.com/myles-megyesi/2012/04/26/… . Going from years of java/javascript to clojure is quite a jarring event. Commented Apr 5, 2014 at 17:51
  • Is it significant that the two functions have the same local name? - If not, why do they have the same pattern of invocation? - If so, is there more than one function sharing this pattern of usage? Commented Apr 6, 2014 at 8:46

1 Answer 1

6

You can use intern to get or create a var in another namespace.

 (intern ns name)
 (intern ns name val)

Finds or creates a var named by the symbol name in the namespace ns (which can be a symbol or a namespace), setting its root binding to val if supplied. The namespace must exist. The var will adopt any metadata from the name symbol. Returns the var.

So you could theoretically make your function do this:

(defn say-hello [target-namespace]
   ((intern target-namespace 'hello)))

However, this would be a very unusual thing to do in Clojure/ClojureScript and you can probably solve your problem more idiomatically using one of the polymorphism options such as protocols or multimethods. Protocols can handle most use cases, and is probably your best starting point (especially if you're coming from Java - they're very interface-like).

Syntax is like this:

(defprotocol Friendly
    (say-hello [this]))

And you can create a number of datatypes that conforms to the protocol using defrecord.

(defrecord person [name]
    Friendly
    (say-hello [this] (println (format "Hi, I'm %s" name)))

(defrecord dog [name]
    Friendly
    (say-hello [this] (println (format "Woof, Woof! I'm %s" name)))

(say-hello (Person. "Bob"))
;=> "Hi, I'm Bob"
Sign up to request clarification or add additional context in comments.

2 Comments

This seems to be the way to go. I'm a little concerned with code organization; since I'm going to have a lot of these and they could be fairly complex, I'd like for them to be in different files, but the convention in ClojureScript (with Leiningen) seems to be putting those different files in different namespaces. Perhaps put the protocol in one file, then have each record type in separate files, then finally have another file that requires all the records and has the constructor functions.
@Depressio You can also organize subfiles without packaging with separate namespaces with load from within a parent file.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.