So after thinking about Alex and James' respective answers I think the
full answer to your question is that no you can't do that for reasons
pretty fundamental to the JVM unless you care to constrain the problem
domain or qualify your question some more.

Making a new class instance is a three step dance:
1. Get all the class initialization values on the stack
2. Use the new [1] instruction to create an instance
3. use invokeSpecial [2] to call the <init> constructor

The Clojure calling convention is logically Object* -> Object except
in the case of JVM primitives [3]. Without my strictly tagged Clojure
work, all Clojure locals are also Object typed at the byte code level
(again unless primitive) and checkCast instructions [4] are used to
dynamically check the tagged types. This means that in the general
case of a function as such:

```clojure
;; totally doesn't work
;; presuming that new was not special and could accept varargs
(defn make-it [class & args]
  (apply new class args))
```

the absolute best bytecode one could possibly emit for this expression
would be to 1, iterate over all the args (because it's a varargs seq
all we know is that they are Objects in a Seq) pushing them onto the
stack last to first. 2, get the class from the arguments, cast it to a
class (checkCast) and then use java.lang.Class.newInstance() [5] to
create an uninitialized instance. Now we have a problem. In order to
initialize and use the new instance, we _must_ invoke the appropriate
<init> method, which may not accept Object typed arguments. In your
own example you had an inline String. Somehow, for instance by using
java.lang.Class.getDeclaredConstructors [6] and looking for
constructors somehow matching the sequence of arguments (which are of
unknown types, all Object) so that we can then invoke it. All we have
on hand is an uninitialized (and probably useless) Object, and a heap
of argument Objects. We can do no better here at least statically than
to use reflection.

This gives the answer of "don't even bother trying to wrap new, just
use it" given by James and Alex. And frankly I agree. Inline new is
the way to preserve and use the most type information, and is
syntactically clear to other programmers.

If you _really really really_ want to do "better" than inline new and
I claim that you really don't, you could use the following code
modified from James' suggestion.

```clojure
;; MIT/X11 license disclaimed
;; - may not work, written late with alcohol
;; - no guarantee for any purpose

(def maybe-type?
  (some-fn nil?    ;; implicitly Object
           symbol? ;; primitive types
           class?  ;; a specific Class
           ))

(defn make-ctor-fn* [class & types]
  {:pre [;; class cannot be nil, we must make something
         (class? class)]}
  (let [args (mapv #(let [c (or % Object)]
                      ;; args must have Types (could be prim) too
                      (assert (maybe-type? c))
                      ;; make a local sym with the tag
                      (with-meta (gensym)
                        {:tag c}))
                   types)]
    `(fn ~(with-meta args {:tag class})
       (new ~class ~@args))))

(def make-ctor-fn
  (memoize
   (fn [& args]
     (eval (apply make-ctor-fn* args)))))
```

which will at least let you cache the dynamically generated
constructor fns and avoid repeatedly calling eval although I make no
claim of a meaningful performance difference.

Issues with this approach include the following:

You have to hit the memoize cache on every call (unless you safe your
constructor fns to a local since I'm assuming you're truly doing
something type dynamic here).

Clojure doesn't (and can't, see Object typed calling convention)
statically know the type of the result of your dynamic constructor fn
so you'll get back an Object. This means that in order to do any
method calls or field accesses you'll have to either use reflection or
do more crazy cached dynamic fn generation in order to work with the
resulting Class instance in a remotely performant fashion.

I claim that whatever you're working on probably isn't actually type
dynamic enough to require any of this weirdness if you stop and think
about it. But it may in which case cached eval of generated fns with
tags may be your best bet.



This was written with no effort made to profile anything just from my
knowledge of the JVM spec, Java standard library and Clojure. YYMV,
get a profiler and look at it for yourself.

Reid



Citations:

[1]
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.new
[2]
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokespecial
[3]
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java
[4]
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.checkcast
[5]
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#newInstance--
[6]
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getDeclaredConstructors%28%29

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