Thanks for the quick response. One issue at a time:
(A) Putting metadata on Vars instead of on the functions themselves: I need to be able to associate facts with the function instances. I can't rely on every function being bound to a Var. For example, I'm constructing cost functions for machine learning, and other applications, by summing, composing, etc. other functions. On Tue, Sep 19, 2017 at 11:34 PM, Alex Miller <a...@puredanger.com> wrote: > > > On Tuesday, September 19, 2017 at 8:01:07 PM UTC-5, John Alan McDonald > wrote: >> >> 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 >> <https://www.google.com/url?q=https%3A%2F%2Fclojure.org%2Freference%2Fmetadata&sa=D&sntz=1&usg=AFQjCNHbXrwRkSRL6pFAprN1DcrQPUEbJA> >> says "Symbols and collections support metadata...". Nothing about >> whether any other types do or do not support metadata. >> > > Functions are probably the big missing thing in that list of types that > have metadata that you can modify. > > A few other things also have metadata that can be set at construction and > read but not modified after construction: namespaces and the reference > types (vars, atoms, refs, agents). > > >> 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/cl >> ojure/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. >> > > I think the tricky thing here is that functions are only equal if they are > identical. Perhaps it would make sense to implement equals in the function > hierarchy to somehow "unwrap" the meta wrappers. I'm not sure if that even > makes sense. > > Another option here is this, which I think would be more typical: > > (def ^{:domain Double/TYPE :codomain Double/TYPE :range {:from 0.0 :to > Double/POSITIVE_INFINITY :also Double/NaN}} meta-square square) > > that puts the meta on the var (meta-square), not on the function instance > itself. In this case equality works and the invocation timing should be > about the same since in both cases you're going through the var to invoke > the identical function. You can retrieve the meta with (meta #'meta-square) > since it's on the var. > > 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). >> > > I see the overhead of your meta-square as more like 2 times the cost in a > quick test, not sure how you're testing. I'm using 1.9.0-beta1 and Java 8 > and timing 100,000 invocations over a series of runs. There's a lot of > variability - using something like Criterium would yield better data. > > >> The reason is that meta-square is an instance of a class that extends >> RestFn (https://github.com/clojure/clojure/blob/master/src/jvm/cloj >> ure/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? >> > > I think it would add the cost of an object ref (prob 32 or 64 bits) to > every function object and I don't think it matters if it's empty or not. I > don't really know the reason for the current design, would require some > research. There are a LOT of potential considerations here with respect to > backwards compatibility, etc. Any change like this would be treated very > carefully. I do not think the need is necessarily worth such a change, but > it's hard to weigh that. > > >> (2) Experiments with a mechanical wrapper class ( >> https://github.com/palisades-lakes/dynamic-functions/blob/d >> ynesty/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. >> > > That's what code gen is for. > > >> (3) Use asm to create a new class that extends the original function's >> class and implements IObj in the obvious way. >> > > The asm included inside Clojure should be considered an internal > implementation detail, subject to version and API changes without warning. > > >> My short term plan is (2), ignoring the equals violation, and >> implementing primitive interface wrappers as needed. >> > > Will the var version above satisfy? > > >> Are there problems with (3) asm, as a long term solution? >> > > As mentioned above, you should not rely on this being available or free > from breakage. > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@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+unsubscr...@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 a topic in the > Google Groups "Clojure" group. > To unsubscribe from this topic, visit https://groups.google.com/d/ > topic/clojure/D8mksieuUPI/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.