Have you seen this?
http://www.assembla.com/wiki/show/clojure/Enhanced_Primitive_Support
•hint for return goes on arg vector
◦e.g. (defn ^:static foo ^long [x] …)
◦this so it supports multi-arity with varied returns
I couldn't find a practical use of doing (defn ^String foo [] ...)
what's hinting the fn name good for?
The code in 1.3 is actually better than I had anticipated since you
get the speed benefits whether or not your arguments are primitives
(see Static linking); I thought you'd need two implementations of
every calls that takes primitives.
You don't mention ^:static and that seems to be required for best
performance.
:static
•defn supports {:static true} metadata
•:static fns can take/return longs and doubles in addition to Objects
•compiler will compile static methods in addition to IFn virtual
methods
The metadata isn't there, so I guess some symbol table is involved in
compiling static functions, not just the var.
user=> (defn fib [n]
(if (<= n 1)
1
(+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (time (fib 38))
"Elapsed time: 25383.208481 msecs"
63245986
user=> (defn ^:static fib ^long [^long n]
(if (<= n 1)
1
(+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (meta (var fib))
{:arglists ([n]), :ns #<Namespace user>, :name fib, :static
true, :line 149, :file "NO_SOURCE_PATH"}
user=> (time (fib 38))
"Elapsed time: 2792.755504 msecs"
63245986
user=> (time (fib 38N))
"Elapsed time: 2777.905013 msecs"
63245986
On May 11, 8:09 am, Chas Emerick <[email protected]> wrote:
> (sorry for the prior hiccup, I accidentally sent a draft of my message)
>
> I've been using 1.3.0 alphas for real work for a little while now, and would
> like to raise a couple of issues around type hinting so as to calibrate my
> expectations and understanding and/or provoke some discussion about some
> inconsistencies I'm seeing.
>
> Some observations from a REPL interaction first:
>
> => *clojure-version*
> {:major 1, :minor 3, :incremental 0, :qualifier "alpha6"}
> => (set! *warn-on-reflection* true)
> true
> => (defn ^String foo [])
> #'user/foo
> => (fn [] (String. (foo)))
> #<user$eval398$fn__399 user$eval398$fn__399@215b011c>
> => (-> #'foo meta :tag)
> java.lang.String
>
> Right, so hinting a var's name works just fine, as in 1.2.x. Things get
> wonky when hinting with primitives, though:
>
> => (defn ^long foo [])
> #'user/foo
> => (fn [] (Long. (foo)))
> #<CompilerException java.lang.IllegalArgumentException: Unable to resolve
> classname: clojure.core$long@41f6321, compiling:(NO_SOURCE_PATH:1)>
> => (-> #'foo meta :tag)
> #<core$long clojure.core$long@41f6321>
>
> The compilation fails because the var's :tag metadata is a function,
> clojure.core/long! Bizarre. I would assume that that's a bug, except that
> it appears the intended way to hint _primitive_ returns (but no other
> returns?) is on the arg vector, and not the var name:
>
> => (defn foo ^long [])
> #'user/foo
> => (fn [] (Long. (foo)))
> #<user$eval410$fn__411 user$eval410$fn__411@18287811>
> => (-> #'foo meta :tag)
> nil
> => (meta @#'foo)
> nil
>
> So our compilation succeeds here, but the var's metadata is left :tag-less
> (understandably, since the hint is on the arg vector and not the var name),
> and the function has no metadata at all (this is surely a bug, but not my
> primary focus here).
>
> My understanding is that the objective in hinting the arg vector is to allow
> for variable return hints on functions with multiple arities (which was not
> possible in 1.2.x). This is an excellent aim, but it would seem that the
> current implementation presents some usage difficulties:
>
> => (defn foo
> (^String [])
> (^long [a])
> (^double [a b]))
> #'user/foo
> => #(String. (foo))
> #<user$eval2241$fn__2242 user$eval2241$fn__2242@25c2cbee>
> Reflection warning, NO_SOURCE_PATH:1 - call to java.lang.String ctor can't be
> resolved.
> => #(Long. (foo))
> #<user$eval2245$fn__2246 user$eval2245$fn__2246@508a8b07>
> Reflection warning, NO_SOURCE_PATH:1 - call to java.lang.Long ctor can't be
> resolved.
> => #(Long. (foo 0))
> #<user$eval2249$fn__2250 user$eval2249$fn__2250@c23c5ff>
> => #(Double. (foo 0))
> #<user$eval2260$fn__2261 user$eval2260$fn__2261@35cfee57>
> Reflection warning, NO_SOURCE_PATH:1 - call to java.lang.Double ctor can't be
> resolved.
> => #(Double. (foo 0 0))
> #<user$eval2264$fn__2265 user$eval2264$fn__2265@5d504a84>
>
> So, we can have variable hinting of fn returns of different arities, but only
> for primitive types. This hurts, and should either be "fixed" or produce a
> compiler warning. Also, there is no metadata on the var indicating the types
> of the different function arities:
>
> => (meta #'foo)
> {:arglists ([] [a] [a b]), :ns #<Namespace user>, :name foo, :line 1, :file
> "NO_SOURCE_PATH"}
>
> Somewhat worse from the standpoint of semantic consistency, hinting the var
> with ^String yields good — yet confusing — results:
>
> => (defn ^String foo
> ([])
> (^long [a])
> (^double [a b]))
> #'user/foo
> => #(Double. (foo 0 0))
> #<user$eval2289$fn__2290 user$eval2289$fn__2290@69996e15>
> => #(String. (foo))
> #<user$eval2293$fn__2294 user$eval2293$fn__2294@220860ba>
>
> And now the var metadata has a :tag, but only for the ^String hint:
>
> => (meta #'foo)
> {:arglists ([] [a] [a b]), :ns #<Namespace user>, :name foo, :line 1, :file
> "NO_SOURCE_PATH", :tag java.lang.String}
>
> I understand that the generated function implements the necessary interfaces
> when primitive hints are involved:
>
> => (supers (class foo))
> #{clojure.lang.IFn$OL java.lang.Object java.lang.Runnable clojure.lang.Fn
> clojure.lang.AFn clojure.lang.IObj clojure.lang.IMeta clojure.lang.AFunction
> java.util.concurrent.Callable clojure.lang.IFn$OOD java.io.Serializable
> java.util.Comparator clojure.lang.IFn}
>
> Having to touch (:tag (meta var)) as well as dig away at the implemented
> interfaces of generated function classes (which I have to assume are strictly
> implementation details) seems unnecessarily inconsistent.
>
> Finally, I'd like to question the current path of supporting hinting of
> primitive returns — and primitive returns only — via function arg vectors.
> Again, to me, it's a simple point of consistency, this time from a mostly
> syntactic perspective; most of the examples below have already been seen
> above, but I repeat them so that they're all co-located for easy comparison:
>
> We hint object returns via the var name:
>
> (defn ^String foo [])
>
> But we hint primitive returns via arg vectors:
>
> (defn foo ^long [])
>
> Different arities of functions can be hinted, but _only_ with primitive
> types, and on arg vectors:
>
> (defn foo
> (^long [])
> (^double [a]))
>
> While a hint added to a function's var "cascades" down to any unhinted
> arities of a function:
>
> (defn ^String foo
> ([] "bar")
> (^double [a])
> ([a b] :surprise!)
>
> I wasn't able to find a canonical discussion of the introduction of this
> semantic of hinting argument vectors, so a pointer to one (or a new one
> here!) would be great. It doesn't make a lot of sense to me — return hints
> are related to the function, not the arguments. I thought that perhaps
> hinting the arg vector was provided so as to support hinting returns of
> anonymous functions, but that doesn't seem to be the case:
>
> => (def a (fn ^long []))
> #'user/a
> => #(Long. (a))
> #<user$eval2390$fn__2391 user$eval2390$fn__2391@3865db85>
> Reflection warning, NO_SOURCE_PATH:1 - call to java.lang.Long ctor can't be
> resolved.
>
> From a naive perspective, understanding that perhaps implementation details
> and historical considerations are driving the current state of things, I
> would expect this to work, and be the most semantically-consistent usage:
>
> (defn foo
> ^String ([] "bar")
> ^double ([a] 5.6)
> ([a b] :not-hinted-at-all))
>
> …with appropriate metadata on #'foo and the function itself indicating the
> corresponding arity<=>return types.
>
> Of course, this isn't how 1.3.0 alphas work now, but unless things are really
> locked down, I hope the above is a reasonable starting point for discussing
> how to smooth out the inconsistencies that currently exist.
>
> Cheers,
>
> - Chas
--
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