How can i define multiple constructors and states using gen-class in clojure?
I do not see a way to do this with single valued mappings for :init, :state and :constructors.
Multiple Constructors
To have multiple constructors on the generated class you need all the constructor parameters specified in the :constructors option of gen-class, and the :init function should be multi-arity to match. Something like the following:
(ns something-amazing
(:gen-class :init myconstructor
:state state
:constructors {[String] []
[String String] []}))
(defn -myconstructor
([^String p1] [[] {:name p1 :special false}])
([^String p1 ^String p2] [[] {:name p1 :special p2}]))
In this case, both constructors would call the same zero-parameter super-type constructor, as specified by the empty vector values in the :constructor hash-map.
Multiple States
State is generally a hash-map, so you don't need multiple states. Just use keywords where you would use field names in an object.
{:name "name1"
:special false}
(defn -method1 [this] (:name (.state this)))
Related
I've defined a record with a bunch of fields--some of which are computed, some of which don't map directly to keys in the JSON data I'm ingesting. I'm writing a factory function for it, but I want to have sensible default/not-found values. Is there a better way that tacking on :or [field1 "" field2 "" field3 "" field4 ""...]? I could write a macro but I'd rather not if I don't have to.
There are three common idioms for implementing defaults in constructor functions.
:or destructoring
Example:
(defn make-creature [{:keys [type name], :or {type :human
name (str "unnamed-" (name type))}}]
;; ...
)
This is useful when you want to specify the defaults inline. As a bonus, it allows let style bindings in the :or map where the kvs are ordered according to the :keys vector.
Merging
Example:
(def default-creature-spec {:type :human})
(defn make-creature [spec]
(let [spec (merge default-creature-spec
spec)]
;; ....
))
This is useful when you want to define the defaults externally, generate them at runtime and/or reuse them elsewhere.
Simple or
Example:
(defn make-creature [{:keys [type name]}]
(let [type (or type :human)
name (or name (str "unnamed-" (name type)))]
;; ...
))
This is as useful as :or destructoring but only those defaults are evaluated that are actually needed, i. e. it should be used in cases where computing the default adds unwanted overhead. (I don't know why :or evaluates all defaults (as of Clojure 1.7), so this is a workaround).
If you really want the same default value for all the fields, and they really have to be different than nil, and you don't want to write them down again, then you can get the record fields by calling keys on an empty instance, and then construct a map with the default values merged with the actual values:
(defrecord MyFancyRecord [a b c d])
(def my-fancy-record-fields (keys (map->MyFancyRecord {})))
;=> (:a :b :c :d)
(def default-fancy-fields (zipmap my-fancy-record-fields (repeat "")))
(defn make-fancy-record [fields]
(map->MyFancyRecord (merge default-fancy-fields
fields)))
(make-fancy-record {})
;=> {:a "", :b "", :c "", :d ""}
(make-fancy-record {:a 1})
;=> {:a 1, :b "", :c "", :d ""}
To get the list of record fields you could also use the static method getBasis on your record class:
(def my-fancy-record-fields (map keyword (MyFancyRecord/getBasis)))
(getBasis is not part of the public records api so there are no guarantees it won't be removed in future clojure versions. Right now it's available in both clojure and clojurescript, it's usage is explained in "Clojure programming by Chas Emerick, Brian Carper, Christophe Grand" and it's also mentioned in this thread during a discussion about how to get the keys from a record. So, it's up to you to decide if it's a good idea to use it)
I have been reading the 'Clojure Rationale' here:
http://clojure.org/rationale
In the Polymorphism section it reads:
Clojure multimethods decouple polymorphism from OO and types
Supports multiple taxonomies
Dispatches via static, dynamic or external properties, metadata, etc
What is meant by 'supports multiple taxonomies' here? I fact, what is a taxonomy in this case? Thanks
user> (defmulti shade :color)
nil
user> (defmethod shade :black [_] "darkest of darks")
#<MultiFn clojure.lang.MultiFn#22b90f93>
user> (defmethod shade :white [_] "all frequencies are reflected")
#<MultiFn clojure.lang.MultiFn#22b90f93>
user> (defmulti movement :legs)
#'user/movement
user> (defmethod movement 2 [_] "walks on hind legs")
#<MultiFn clojure.lang.MultiFn#13b58075>
user> (defmethod movement 4 [_] "proceeds on all fours")
#<MultiFn clojure.lang.MultiFn#13b58075>
user> ((juxt movement shade) {:name "cat" :legs 4 :color :black})
["proceeds on all fours" "darkest of darks"]
In the above code, two systems of organization are created - one in terms of color, the other in terms of legs. Both of these taxonomies are equally valid, and the same object can fall into different places depending on the taxonomy used.
Multimethods can also use hierarchies for dispatch (see derive and related functions), where each hierarchy can co-exist in parallel (unlike the unified view of Class hierarchies).
user> (derive ::manx ::cat)
nil
user> (defmulti favorite-treat :species)
#'user/favorite-treat
user> (defmethod favorite-treat ::cat [_] "Tuna")
#<MultiFn clojure.lang.MultiFn#264d27e6>
user> (derive ::indoor ::domestic)
nil
user> (defmulti activity :tameness)
#'user/activity
user> (defmethod activity ::domestic [_] "window watching")
#<MultiFn clojure.lang.MultiFn#1654bf3f>
user> ((juxt favorite-treat activity) {:species ::manx :tameness ::indoor})
["Tuna" "window watching"]
here the same kitty is a member of two hierarchies - one of domestication, and the other of genetics, and is can have its methods resolved by either as appropriate.
Also, even setting asid multimethods, relationships created via derive support multiple inheritance, unlike the jvm Class heirarchy Clojure is built on:
user> (derive ::foo ::bar)
nil
user> (derive ::foo ::baz)
nil
user> (derive ::quux ::foo)
nil
user> (isa? ::quux ::foo)
true
user> (isa? ::quux ::bar)
true
user> (isa? ::quux ::baz)
true
user> (isa? ::bar ::baz)
false
Clojure, like many other functional languages, takes advantage of loose typing to provide easy ways to implement parametric polymorphism. This basically means that a single method can be constructed in a way that it does not care about the types of values the arguments it is given are.
Take for example the concat method. It takes any number of arguments of varying forms and puts them into a single list like so:
user=> (concat [:a :b] nil [1 [2 3] 4])
(:a :b 1 [2 3] 4)
Because one does not need to declare a typing for arguments, concat can be written in such a way that you can provide an argument of any type (vector, function, keyword, string, etc.) and it will act upon them in a similar way.
Clojure's multimethods allow you to support this concept on Java objects that may have completely different structures (i.e. taxonomies) by using metadata or other properties to determine the appropriate way to deal with the given argument. See the following example (taken from defmulti):
(defmulti greeting
(fn[x] (x "language")))
(defmethod greeting "English" [params]
(str "Hello! " (params "id")))
(defmethod greeting "French" [params]
(str "Bonjour! " (params "id")))
=>(greeting {"id" "1", "language" "English"})
"Hello! 1"
=>(greeting {"id" "2", "language" "French"})
"Bounjour! 2"
The greeting method returns the "language" value from the map which matches the "English" or "French" defmethod which returns the correct corresponding value. Hopefully, you can see how this concept could potentially be applied to just about any kind of data or object structure. This powerful idea of polymorphism is what the Clojure developers are trying to show off in the rationale page.
in the :constructors map and subsequent -init definitions, how do I represent a varargs constructor (assuming the superclass has multiple constructors of which one is varargs) ?
Since varargs are essentially syntax sugar for Object arrays, you could just use "[Ljava.lang.Object;" as the type of constructor's parameter.
Here's some sample code:
(ns t.vtest
(:gen-class
:implements [clojure.lang.IDeref]
:init init
:state state
:constructors {["[Ljava.lang.Object;"] []}))
;; ^-----------------------
;; You should put "[Ljava.lang.Object;" for superclass varargs constructor here
;; I left it blank for the sake of working example
(defn -init
[args]
(println "first element of args" (aget args 0) "total elements" (alength args))
[[] (into [] args)])
(defn -deref
[this]
(.state this))
and that's how it looks in REPL
user=> #(t.vtest. (into-array Object ["A" "B" 1 2]))
first element of args A total elements 4
["A" "B" 1 2]
Since clojure don't support it at the moment you need to patch it with: https://groups.google.com/forum/#!topic/clojure/HMpMavh0WxA.
And use it with new meta tag:
(ns t.vtest
(:gen-class
:implements [clojure.lang.IDeref]
:init init
:state state
:constructors {^:varargs ["[Ljava.lang.Object;"] []}
))
I have a simple record definition, for example
(defrecord User [name email place])
What is the best way to make a record having it's values in a sequence
(def my-values ["John" "john#example.com" "Dreamland"])
I hoped for something like
(apply User. my-values)
but that won't work. I ended up doing:
(defn make-user [v]
(User. (nth v 0) (nth v 1) (nth v 2)))
But I'm sensing there is some better way for achieving this...
Warning: works only for literal sequables! (see MihaĊ's comment)
Try this macro:
(defmacro instantiate [klass values]
`(new ~klass ~#values))
If you expand it with:
(macroexpand '(instantiate User ["John" "john#example.com" "Dreamland"]))
you'll get this:
(new User "John" "john#example.com" "Dreamland")
which is basically what you need.
And you can use it for instantiating other record types, or Java classes. Basically, this is just a class constructor that takes a one sequence of parameters instead of many parameters.
the defrecord function creates a compiled class with some immutable fields in it. it's not a proper clojure functions (ie: not a class that implements iFn). If you want to call it's constructor with apply (which expects an iFun) you need to wrap it in an anonymous function so apply will be able to digest it.
(apply #(User. %1 %2 %3 %4) my-values)
it's closer to what you started with though your approach of defining a constructor with a good descriptive name has its own charm :)
from the API:
Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.
Writing your own constructor function is probably the way to go. As Arthur Ulfeldt said, you then have a function you can use as a function (e.g. with apply) rather than a Java-interop constructor call.
With your own constructor function you can also do argument validation or supply default arguments. You gain another level of abstraction to work with; you can define make-user to return a hash-map for quick development, and if you later decide to change to records, you can do so without breaking everything. You can write constructors with multiple arities, or that take keyword arguments, or do any number of other things.
(defn- default-user [name]
(str (.toLowerCase name) "#example.com"))
(defn make-user
([name] (make-user name nil nil))
([name place] (make-user name nil place))
([name user place]
(when-not name
(throw (Exception. "Required argument `name` missing/empty.")))
(let [user (or user (default-user name))]
(User. name user place))))
(defn make-user-keyword-args [& {:keys [name user place]}]
(make-user name user place))
(defn make-user-from-hashmap [args]
(apply make-user (map args [:name :user :place])))
user> (apply make-user ["John" "john#example.com" "Somewhere"])
#:user.User{:name "John", :email "john#example.com", :place "Somewhere"}
user> (make-user "John")
#:user.User{:name "John", :email "john#example.com", :place nil}
user> (make-user-keyword-args :place "Somewhere" :name "John")
#:user.User{:name "John", :email "john#example.com", :place "Somewhere"}
user> (make-user-from-hashmap {:user "foo"})
; Evaluation aborted.
; java.lang.Exception: Required argument `name` missing/empty.
One simple thing you can do is to make use of destructuring.
(defn make-user [[name email place]]
(User. name email place))
Then you can just call it like this
(make-user ["John" "John#example.com" "Dreamland"])
Update for Clojure 1.4
defrecord now defines ->User and map->User thus following in Goran's footstaps, one can now
(defmacro instantiate [rec args] `(apply ~(symbol (str "->" rec)) ~args))
which also works with non-literal sequences as in (instantiate User my-values).
Alternatively, along the lines of map->User one can define a function seq->User
(defmacro def-seq-> [rec] `(defn ~(symbol (str "seq->" rec)) [arg#] (apply ~(symbol (str "->" rec)) arg#)))
(def-seq-> User)
which will allow (seq->User my-values).
The idiomatic way to call a Record constructor is with the Clojure symbol ->MyRecord and that works just fine with apply.
(def my-values ["John" "john#example.com" "Dreamland"])
(defrecord User [name email place])
(apply ->User my-values)
; => #my-ns.User{:name "John",
:email "john#example.com",
:place "Dreamland"}
I'm learning how to extend Java classes in Clojure, but I don't see a way declare new member variables; I only see a way for methods.
(ns test.aclass
(:gen-class
:methods [[foo [] String]]))
Is there a :members keyword or another way of declaring member variables?
:state name
If supplied, a public final instance field with the given name will be created. You must supply an :init function in order to provide a value for the state. Note that, though final, the state can be a ref or agent, supporting the creation of Java objects with transactional or asynchronous mutation semantics.
There is an example on the website of how it can be used.
I was having some trouble with this too. The example below is not elegant but it's pretty simple for writing dumb little glue classes in Clojure rather than Java. Note all I did for thread safety is to ensure that the field updates are atomic -- I didn't do any other concurrency stuff, and that may make a real difference.
The init method creates the instance variables for the object. The setfield and getfield macros abbreviate the bookkeeping of the atomic update.
(ns #^{:doc "A simple class with instance vars"
:author "David G. Durand"}
com.tizra.example )
(gen-class
:name com.tizra.example.Demo
:state state
:init init
:prefix "-"
:main false
:methods [[setLocation [String] void]
[getLocation [] String]]
)
(defn -init []
"store our fields as a hash"
[[] (atom {:location "default"})])
(defmacro setfield
[this key value]
`(swap! (.state ~this) into {~key ~value}))
(defmacro getfield
[this key]
`(#(.state ~this) ~key))
(defn -setLocation [this ^java.lang.String loc]
(setfield this :location loc))
(defn ^String -getLocation
[this]
(getfield this :location))
You have to compile this, and make sure the stub class produced is on your classpath, and then you can make instances, etc. just like any other java class.
=> (com.tizra.example.Demo.)
#<Demo com.tizra.example.Demo#673a95af>
=> (def ex (com.tizra.example.Demo.))
#'user/ex
=> (.getLocation ex)
"default"
=> (.setLocation ex "time")
nil
=> (.getLocation ex)
"time"
I found the longer summary at this blog quite helpful: http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html
The body of a proxy is a lexical closure, so you can just close around whatever variables you need. If, God forbid, you need to mutate them, then close around an atom:
(defn lying-list [init-size]
(let [the-size (atom init-size)]
(proxy [java.util.ArrayList] []
(size [] #the-size)
(remove [idx] (reset! the-size idx), true))))
There's really no need for actual Java fields here.