views:

124

answers:

2

This question is closely related to this one but i think is more general.

Recently i try to create type "instances" on the fly with multimethods (or with a unique function constructor if possible), based in a metadata tag. I linked a type (a java class under the hood) with this tag and then i didnt know how to continue in a elegant way (without eval or java reflection and strings); new is a special form and it's difficult to handle although you use macros.

In seudo-code it would be:

(def my-tagged-data (with-meta my-data {:my-type-tag my-ns.My-Type}))
(def factory-function [tagged-data] 
     (create (:my-type-tag (meta tagged-data)) tagged-data))
A: 

I think that you have to use reflection. (Better idea than eval, I think).

Nicolas Oury
Moreover, i am missing something obvious cause i dont catch how to use newInstance, cause Integer/class dont work neither (Integer/newInstance)
jneira
+2  A: 

You can provide factory functions for your types.

(def my-tagged-data (with-meta my-data {:my-type-factory `my-type-factory}))

(defn factory-function
  [tagged-data] 
  (@(resolve (:my-type-factory (meta tagged-data))) tagged-data))

This may or may not be feasible.

kotarak
is the @ in front of resolve a typo or a hack unknown for me?? i had paid no attention to resolve, thank for the point.
jneira
the @ is dereffing the var returned by resolve but is optional in this case because you can directly call a var.
cgrand
kotarak idea is good but instaed of a symbol you can directly put the factory into metadata and use a global registry as a fallback for legacy classes.
cgrand
well, i think it is not exactly that i want but i think the response has more sense than the question and it is suggestive
jneira
@cgrand: however putting the factory directly in the metadata is more static. Changing the factory will then only affect fresh my-data's while the symbol approach also affects existing my-data's. YMMV. Also fns are not printable? Is this of interest?
kotarak
@kotarak if you want that dynamicity then use the Var itself instead of its value or its symbol. Best of both worlds?
cgrand
@kotarak PS: I hate resolve
cgrand
@cgrand It has its uses. But I agree. I normally try to avoid this reflection kind of stuff.
kotarak
cgrand
I have a project with several models, which have to be initialised in the database (in case of a "fresh" db). Since I use clj-record for this project, I can use a simple name to retrieve things. To just name the names once, I used `resolve` to retrieve the model information for table creation etc.: `(let [desc @(resolve (symbol (str "my.project.models" model-name) "description"))] (create-table ....))`. I think this is the only case I ever used `resolve`. Maybe not the best approach, but it worked. I'm deeply suspicious about code using `resolve`, `intern` and friends.
kotarak