Private multimethods possible?

1,458 views
Skip to first unread message

samppi

unread,
Oct 22, 2009, 10:39:38 PM10/22/09
to Clojure
Are private multis possible? I notice that clojure.contrib.def does
not have a defmulti-, which doesn't bode well, but it's still worth a
question at the mailing list.

Alex Osborne

unread,
Oct 22, 2009, 11:07:22 PM10/22/09
to clo...@googlegroups.com

Yes, you can make any symbol private. If you look at the definition of
defn- you'll see all it does is set the :private metadata entry on the
function's name to true:

(defmacro defn-
"same as defn, yielding non-public def"
[name & decls]
(list* `defn (with-meta name (assoc (meta name) :private true)) decls))

This bears repeating as it may not be obious: symbols *themselves* are
set private, not the functions (or other values) they are bound to.

So you could do the same when defining a multimethod, just give the name
(symbol) of the method the metadata ":private" with the value "true":

(defmulti #{:private true} my-multi my-dispatch)

Alex Osborne

unread,
Oct 22, 2009, 11:11:19 PM10/22/09
to clo...@googlegroups.com
> So you could do the same when defining a multimethod, just give the
> name (symbol) of the method the metadata ":private" with the value
> "true":
>
> (defmulti #{:private true} my-multi my-dispatch)

I'm having a bad day for typos, the example should of course be:

(defmulti #^{:private true} my-multi my-dispatch)

John Harrop

unread,
Oct 23, 2009, 2:08:08 AM10/23/09
to clo...@googlegroups.com
I think we need some notion of semi-private as well. It would be ignored by :use and by automation like tab-completion of symbols, doc generation, and the like (except it would show in tab-completion inside of its namespace) but would not actually be illegal to invoke from elsewhere. So, it would be like private in every respect except for invokability.

The purpose being so that you can make support functions (and perhaps other things) for macros without cluttering up the publicly-visible symbols list of the namespace, that is the set exported via :use and :require :as and affecting tab-completion and documentation generation.

Alternatively, perhaps it would be okay to just get rid of the exception when a private var is dereferenced outside its namespace? Is it really important to absolutely bar access to such? Or should private be more about avoiding clutter in exports, docs, tab-completion, and so forth and about documenting what's meant to be used as public API, but not really about access restriction per se?

Alex Osborne

unread,
Oct 23, 2009, 2:37:06 AM10/23/09
to clo...@googlegroups.com
John Harrop wrote:
> I think we need some notion of semi-private as well. It would be ignored
> by :use and by automation like tab-completion of symbols, doc
> generation, and the like (except it would show in tab-completion inside
> of its namespace) but would not actually be illegal to invoke from
> elsewhere. So, it would be like private in every respect except for
> invokability.

I'd tentatively agree. Although perhaps another model would be to just
put your semi-private declarations in a different namespace which is not
normally :use'd.

If you're really desperate you can get around the exception like this:

user> (apples/foo 4)

var: #'apples/foo is not public
[Thrown class java.lang.IllegalStateException]

user> (defmacro reveal [sym]
`(deref ~(clojure.lang.Compiler/resolveIn *ns* sym true)))

user> ((reveal apples/foo) 4)

But that's no doubt a bad idea.

Christophe Grand

unread,
Oct 23, 2009, 2:45:46 AM10/23/09
to clo...@googlegroups.com
Hi John,


On Fri, Oct 23, 2009 at 8:08 AM, John Harrop <jharr...@gmail.com> wrote:
I think we need some notion of semi-private as well. It would be ignored by :use and by automation like tab-completion of symbols, doc generation, and the like (except it would show in tab-completion inside of its namespace) but would not actually be illegal to invoke from elsewhere. So, it would be like private in every respect except for invokability.

The purpose being so that you can make support functions (and perhaps other things) for macros without cluttering up the publicly-visible symbols list of the namespace, that is the set exported via :use and :require :as and affecting tab-completion and documentation generation.


Other solutions are to use @#'ns/private-var to access private vars from the macro or to make the macro shallow using a public (usually higher-order) helper function (is this possible in the general case?).

Christophe

Meikel Brandmeyer

unread,
Oct 23, 2009, 7:41:13 AM10/23/09
to Clojure
Hi,

On Oct 23, 8:45 am, Christophe Grand <christo...@cgrand.net> wrote:

> Other solutions are to use @#'ns/private-var to access private vars from the
> macro or to make the macro shallow using a public (usually higher-order)
> helper function (is this possible in the general case?).

It is not 100% possible in the general case but it the vast majority
of the cases, I'd say.

Concerning the private Vars: I'd actually suggest to dispose them
entirely. Namespaces can be split into an interface space contain the
public interface. It can 'use' a special internal namespace, which
does not belong to the public API of the library. All defs are normal,
public defs.

This approach solves all problems:
- The user knows, what the public API is.
- The macros can access everything they need.
- Code completion only finds the public stuff.
- It doesn't need modifications like a 'semi-private' or 'protected'
flag.

I think, Stephen exercised this approach in c.c.sql.

Sincerely
Meikel

PS: My opinion: design your library to be used with require and :as
instead of use. Document the public API. That is the contract. The
contract stays valid whether the system enforces it or not.

Didier

unread,
Jan 9, 2017, 7:19:16 PM1/9/17
to Clojure, m...@kotka.de
How would you declare a namespace within a namespace? Or two namespaces in the same file?

Meikel Brandmeyer (kotarak)

unread,
Jan 10, 2017, 5:26:01 AM1/10/17
to Clojure, m...@kotka.de


Am Dienstag, 10. Januar 2017 01:19:16 UTC+1 schrieb Didier:
How would you declare a namespace within a namespace? Or two namespaces in the same file?


You can do so quite easily.

  (ns foo.bar)

  (defmulti a :dispatch)

  (defn b
    [x]
    (str "Called a. This was the result: " (a x)))

  (ns foo.bar.hidden)
  (alias 'parent 'foo.bar)

  (defmethod parent/a :yoyo
    [x]
    (str "yoyo: " (:value x)))

  (defmethod parent/a :dyne
    [x]
    (str "dyne: " (:value x)))

  ; Go back to original namespace.
  (in-ns 'foo.bar)


As you can try in the repl, this works.

  user=> (require 'foo.bar)
  nil
  user=> (foo.bar/b {:dispatch :yoyo :value 5})
  "Called a. This was the result: yoyo: 5"
  user=> (foo.bar/b {:dispatch :dyne :value 5})
  "Called a. This was the result: dyne: 5"

That said: I wouldn't recommend this. I'm not sure, I see a situation that would justify the weirdness and hidden complexities of this approach. Too much rope... If you really need this style of thing, put the hidden namespace in its own file and use load.

Kind regards
Meikel

Didier

unread,
Jan 11, 2017, 2:03:16 PM1/11/17
to Clojure
Maybe you're right in not recommending this, but I find it at first glance to be quite nice. Now, I wouldn't keep switching namespace back and forth, but having two sections in the file, one the public API at the top, and everything else at the bottom in a private namespace, that's quite nice I'd say.

I hate juggling through files, like C++ header and body files annoy me a lot.

Andy Fingerhut

unread,
Jan 12, 2017, 1:12:52 PM1/12/17
to clo...@googlegroups.com
At least a few tools built on top of Clojure (not part of the base language implementation) might not work with multiple namespaces defined in the same file.

The library tools.namespace, for one, looks for only the first ns form in each source file, and assumes there are no more after that.  I do not know for sure, but this might negatively impact the handling of multi-namespace source files in Stuart Sierra's reloaded workflow, which uses tools.namespace to determine the order to unload and reload namespaces.

The Eastwood lint tool is very explicit about checking that the file name and namespace within the file have corresponding file/namespace names, and won't give any useful information other than about such mismatches, if they are found.

I am not saying those are fundamental limitations of those tools that couldn't be improved (I don't know if they could -- maybe), but something to watch out for at least with their current implementations.

Andy

On Wed, Jan 11, 2017 at 11:03 AM, Didier <did...@gmail.com> wrote:
Maybe you're right in not recommending this, but I find it at first glance to be quite nice. Now, I wouldn't keep switching namespace back and forth, but having two sections in the file, one the public API at the top, and everything else at the bottom in a private namespace, that's quite nice I'd say.

I hate juggling through files, like C++ header and body files annoy me a lot.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages