Welcome! Please see the About page for a little more info on how this works.

0 votes
ago in Syntax and reader by

TIL:

(eval (list + 1 2)) ; works
(eval (list (var-get #'+) 1 2)) ; works
(eval (list (fn [a b] (+ a b)) 1 2)) ; works

(defmacro m [] `(~(fn [a b] (+ a b)) 1 2))
(m) ; also works

;; however, if the function has any metadata:
(eval (list (with-meta + {}) 1 2))
;; ^ fails, No matching ctor found for class clojure.lang.AFunction$1

This was unintuitive to discover.

I solved it locally with a little helper:

(defn val->expr [v]
  (if (symbol? v)
    `'~v
    (cond-> v
      (and (fn? v) (not (nil? (meta v))))
      (-> (with-meta nil)
          (list (-> v meta (update-vals val->expr)))
          (conj `with-meta)))))

But this takes special handling. I wonder if something similar would not be possible on a "matching ctor"-level, possibly also for comp, every-pred?, etc. It would be fairly trivial even in userland to have versions of these functions which carry metadata sufficient to recreate expressions, e.g. comp storing the list of functions, and then eval, on encountering No matching ctor found for class clojure.core$comp$fn__5921, can instead pull out the list of functions from that value and evaluate (list* comp (:composed-fns (meta v))) (or some such) instead.

I think this would be great to have in the core lib, at least for metadata and core libs functions like comp, some-fn, every-pred?, and complement.

Note, I'm not talking about adding or changing ctors for these necessarily. A hook for taking unconstructable values and making a constructable value out of them would serve just as well, maybe even a multifn.

Strictly in userland and without core lib support, I suppose the cleanest way to do something like this is a custom protocol and probably monkeypatch some core libs functions (maybe eval?)

So I suppose my questions are:

  1. Is this something you agree would be good to have in general? Or is this by design for some reason I can't see?
  2. Would this be good and practical to have in the corelib? Oughtn't be a breaking change at least
  3. If no, is there currently any sane way to apply my solution in a more systemic way than wrapping every relevant value with a val->expr call?

Please log in or register to answer this question.

...