I'd like to be able to do something like:
(defn square ^double [^double x] (* x x))
(def meta-square (with-meta square {:domain Double/TYPE :codomain Double/TYPE
:range {:from 0.0 :to Double/POSITIVE_INFINITY :also Double/NaN}})
https://clojure.org/reference/metadata says "Symbols and collections
support metadata...". Nothing about whether any other types do or do not
support metadata.
The code above works, at least in the sense that it doesn't throw
exceptions, and meta-square is a function that returns the right values,
and has the right metadata.
That's because square is an instance of a class that extends AFunction,
which implements IObj (
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFunction.java#L18).
It doesn't work, in the sense that it violates "Two objects that differ
only in metadata are equal." from https://clojure.org/reference/metadata.
That is,
(= square meta-square)
returns false.
For my purposes, what really matters is that calling meta-square has
roughly 30 times the cost of square itself (and about 3 times the cost of a
version without type hints).
The reason is that meta-square is an instance of a class that extends
RestFn
(https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFunction.java#L26),
whose invoke() methods are expensive.
Also, for my purposes, it would actually be better if "Two objects that
differ only in metadata are NOT equal." So perhaps I shouldn't be using
metadata at all. It just seems
Options:
(1) Add a meta field to clojure.lang.AFunction (and fix equals and
hashcode). I presume the reason there isn't already a meta field is to keep
functions as light weight as possible. Are there good benchmarks that I
could use to measure the cost of adding an almost always empty field?
(2) Experiments with a mechanical wrapper class
(https://github.com/palisades-lakes/dynamic-functions/blob/dynesty/src/main/java/palisades/lakes/dynafun/java/MetaFn.java)
show almost no overhead, but extending that to cover every possible
combination of clojure.lang.IFn$DD, clojure.lang.IFn$DLD, ..., is
impractical.
(3) Use asm to create a new class that extends the original function's
class and implements IObj in the obvious way.
My short term plan is (2), ignoring the equals violation, and implementing
primitive interface wrappers as needed.
Are there problems with (3) asm, as a long term solution?
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.