On Nov 6, 4:31 pm, Daniel Spiewak <[EMAIL PROTECTED]> wrote:
> I've been perusing Stu Halloway's beta of "Programming Clojure" and I
> came across the following example:
>
> (defn describe-class [c]
>   {:name (.getName c)
>    :final (java.lang.reflect.Modifier/isFinal (.getModifiers c))})
>
> As demonstrated by the *warn-on-reflection* flag, Clojure is unable to
> determine a type for c, therefore all (or rather, almost all) of the
> calls made upon it must be handled reflectively.  The performance
> "solution" suggested in the book is just to annotate the c parameter
> with a type:
>
> (defn describe-class [#^Class c] ...)
>
> This is a perfectly valid solution, but it seems a little ugly to me.
> The thought that I had is perhaps the Clojure compiler could actually
> infer this type automatically.  Languages like ML and Haskell have
> been doing this for decades.  Traditionally, Hindley-Milner isn't
> applied to object-oriented languages (or rather, languages which deal
> with uncontrolled OO constructs), but from what I understand, most of
> the reasoning behind this does not apply to Clojure.  For example,
> Clojure doesn't have structural or parametric types because of its
> dynamic nature.  There would be no *need* to infer any type parameters
> on calls out to Java, so the type system could be treated as nominal.
>
> The algorithm I'm thinking of would be something like this:
>
> * Maintain a multi-map of method and field names to classes (this
> could be done incrementally based on what classes the compiler knows
> are in scope)
> * For each parameter with an unfixed type:
>    * Traverse the FnExpr AST and find all Java member access to that
> parameter
>    * For each access, obtain a set of possible classes from the scope
> multi-map
>    * At each step, intersect the previous set of possible classes with
> the subsequent one (think: a fold)
>    * Final set can be optimized by choosing the most-specific type
> (eliminating the inheritance case)
>    * If the final set has size of 1, then the type has been inferred
> and can be used to avoid reflection
>    * If the set has size 0, then fallback on reflection
>    * If the set has size >1, either fallback on reflection or get more
> sophisticated
>
> Improvements on this could be added to the >1 case where the second
> pass could look for methods of a given name *and* a specific arity.
> Subsequent passes could also incorporate inferred types for the method
> parameters.
>
> It's not a complete inference algorithm, but it doesn't have to be.
> There's nothing stopping Clojure from falling back on reflection for
> unfixed types.  The whole logic behind this dance is to avoid
> reflection as much as possible, producing a fairly serious
> optimization in Java-interop without requiring those ugly type
> annotations.
>
> I briefly looked at implementing this myself, but Clojure's compiler
> code is...well, it's a compiler.  :-)  At first glance, I couldn't
> even figure out where to place the appropriate hooks to kick off this
> process.  A better question though is: has this been tried/considered
> before?  What are your overall thoughts with regards to this?

It's not a bad idea, and I'm sure Clojure could do more inference than
it does, but the devil's in the details as usual.

You'd want the least specific type.

The best you could do is make a type-inferred fastpath, since you
could always be wrong:

  In the case above, let's say you had imported Class (actually
Clojure did that for you), and nothing else you imported had getName
and getModifiers methods. Making the inference 'must be Class' could
be wrong, since the function should work fine on
java.lang.reflect.Method, for instance, and maybe that was the intent.

So you'd need a runtime instanceof test for Class, and use the
fastpath if true, reflection if not.

Perf could be harder to pin down, as adding an import could cause
previously fast code to get slow.

Calls can be made only in specific conditional branches, which some
higher-level logic might understand maps to non-overlapping types, so
the inference should be of the call site, not the parameter itself -
i.e. it's perfectly fine to write fns that expect a set of unrelated
types, without being duck-typed. In fact it's even ok to type hint it
even though the hint is only correct in one path.

You have to allow for by-name duck-type calls or call them out
specifically. Right now they are allowed.

I think a simple version of this would be easy to do, when I get some
time.

I think it hasn't been a priority as it takes very few hints at the
moment to remove reflection, given that types are tracked from calls
to new and static calls, and from any other hinted calls. But it would
be great to not need them at all!

Rich
--~--~---------~--~----~------------~-------~--~----~
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
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to