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.

Reply via email to