Clojure record constructors not first class? - clojure

Apparently, you can't call apply with a record constructor:
(defrecord Foo. [id field])
(apply Foo. my-list)
fails at read time because it is not expecting Foo. in that place.
The only obvious workaround I could think of was to add a factory function:
(make-foo [id field] (Foo. id field))
which can be apply'ed of course.
Am I missing anything? I'd expect this from C#/Java but just thought it was a bit disappointing in Clojure...

Circling back on this post-1.3....
In Clojure 1.3, defrecord creates two generated constructor functions. Given:
(defrecord Person [first last])
this will create a positional constructor function ->Person:
(->Person "alex" "miller")
and a map constructor function map->Person:
(map->Person {:first "string"})
Because this is a map, all keys are optional and take on a nil value in the constructed object.
You should require/use these functions from the ns where you declare the record, but you do not need to import the record class as you would when using the Java class constructor.
More details:
http://dev.clojure.org/display/design/defrecord+improvements
http://groups.google.com/group/clojure/browse_thread/thread/ce22faf3657ca00a/beb75e61ae0d3f53

Foo. is a Java class constructor so it has typical Java interop constraints with how you call it. Creating a constructor function is a common solution (it also means you don't have to import the Foo when in a different namespace).

The problem is known and there is lots of talk about it on the Clojure mailing list. More support will probably be added in future Clojure versions.
For now you have to use your own functions or use https://github.com/david-mcneil/defrecord2 which supports some features like:
print in an eval'able form
provide clojure function as constructor
accept named parameters (maps) in constructor
participate in pre/post walk multi-method

Related

Creating new Java object instantiation error

I am trying to create a new SimpleLinkResolver in Clojure. This is the JavaDoc:
http://prismicio.github.io/java-kit/io/prismic/SimpleLinkResolver.html
My clojure code is:
(def lr (new io.prismic.SimpleLinkResolver))
but at the repl I get the following error:
CompilerException java.lang.InstantiationException, compiling:(form-init460449823042827832.clj:1:1)
I have no problem creating a java.util.Date:
(def d (new java.util.Date))
=> #'prismic-clojure.core/d
d
=> #inst"2018-03-17T10:30:36.016-00:00"
The above JavaDoc does say that SimpleLinkResolver is deprecated because the interface LinkResolver (http://prismicio.github.io/java-kit/io/prismic/LinkResolver.html) has default methods and so can be implemented directly. So I gave this a go to:
(def lr (new io.prismic.LinkResolver))
CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface io.prismic.LinkResolver,
And I get this "no ctor" error - which I am guessing means the compiler can't find a constructor?
Questions:
Why does the first effort produce an InstantiationException?
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
Thanks
Why does the first effort produce an InstantiationException?
You can't instantiate an abstract class:
public abstract class SimpleLinkResolver
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
You'll need to implement LinkResolver interface, which can be done using Clojure's reify:
(def resolver
(reify LinkResolver
(^String resolve [this ^Fragment$DocumentLink link]
"a string"))) ;; put actual impl. here
(.resolve resolver nil)
;; => "a string"
Note you need to type-hint the return value (and arguments) because .resolve() is an overloaded method.
Also, you typically see (Class.) dot-syntax rather than (new Class) to instantiate Java classes.

Can't refer to Java interop methods when not calling them [duplicate]

How can I use java methods as a functions arguments in Clojure?
For example, I want to make a functions composition:
user> (Integer. (str \9))
9
user> ((comp Integer. str) \9)
CompilerException java.lang.ClassNotFoundException: Integer., compiling:(NO_SOURCE_PATH:1:2)
That does not work.
memfn doesn't help also:
user> (map (comp (memfn Integer.) str) "891")
IllegalArgumentException No matching method found: Integer. for class java.lang.String clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
Any ideas?
Related questions (that, though, do not give the right answer to the question):
Using interop constructor in map function(Clojure)
Why does Clojure say "no matching method" for an illegal argument?
How do I use Clojure memfn with a Java constructor?
How to get an Fn which calls a Java method? (has a nice explanation in the answers)
(Note: it seems to be that the answer suggested by dave, using of an anonymous function as a wrapper, is the best solution)
Unlike Clojure functions, Java methods weren't designed to be first class. When you use Java inter-op in Clojure, you're literally working with Java methods, so you don't get the added benefits that were implemented for Clojure functions. For more info, see the comments below.
As a workaround to use Java methods as arguments, you can wrap them in an anonymous function, like this, effectively making them Clojure functions:
((comp #(Integer. %) str) \9)

Clojure: Should I prepend new- or make- to constructor functions?

When creating a new Java object via a wrapper function, what is the "constructor" naming standard? Should I prepend make- or new- to the function name? Or just call it the type of thing it returns? At one point I got accustomed to using make- because it's the way it was done in the Scheme book SICP.
For example, I'm passing some stuff to a function which eventually returns an instance of a Java class. Here are a few examples:
(def myimage1 (make-image "img.svg" 100 200)) ; These all return
(def myimage2 (new-image "img.svg" 100 200)) ; an instance of
(def myimage3 (image "img.svg" 100 200)) ; javafx.scene.image.Image
Is it the same for creating Clojure-only structs, such as maps, etc?:
(def mystruct1 (make-custom-struct args))
(def mystruct2 (new-custom-struct args))
(def mystruct3 (custom-struct args))
I prefer the last version without make- or new-, but often times the binding (for example inside a let) would have the same name, which would suggest prepending the constructor name, or using a different binding name, such as:
(let [image (make-image "img.svg" 100 200)] ...)
(let [imuj (image "img.svg" 100 200)] ...)
However other times I'd just like to use the function in-line without the cluttering of the new- or `make-':
(do-something-with-an-image (image "img.svg" 100 200))
Stuart Sierra suggests not using a prefix. His idea is that a pure function can be replaced with its implementation, and so a simple noun makes a better name in that sense than a verb.
I agree that (image ...) is the version that looks the best. However, you are right that you can easily end up shadowing the function with local variables. What can you do?
Namespaces are here to counter the issue introduced by the Lisp-1 nature of Clojure:
(let [image (png/image "file.png")] ...)
Alternatively, when you are in the same namespace, you should give better variable names: image is a little bit generic; try something more precise, like avatar or thumbnail. Note also that sometimes image is the best name to give to the variable.
You can also prefix or suffix variable names, like user-image, old-image or new-image.
Regarding functions, the make- prefix is not bad: it is readable, unambiguous and it fixes the problem of shadowing existing bindings. So don't discard it too quickly. This prefix tends to be found more often than new-, which is a little unclear: sometimes you have old and new data, where new is an adjective.
I think -> is a fun prefix, and it's already used by the factory functions generated by defrecord.

How do I tag a Clojure function so that I could recognize it with Java reflection

I need to somehow tag certain Clojure functions as "special" so that Java code could recognize them as such using reflection. I've tried to add an annotation to a function, but apparently that's not supported. I've tried to reify an interface extending IFn (so that the Java code could recognize the function object), but that's no good because Clojure doesn't directly use the reified method as the code implementing invoke, but rather an indirect call to an Afunction object that's actually implementing the method (I need to tag the actual invoke method with the actual function code).
Any ideas?
EDIT: even tagging in a way that could be accessed with the ASM library (rather than regular reflection) would be fine, but I need to somehow tag the actual AFunction object or the invoke method. Also, I can't access the actual AFunction object -- I need the tag to be visible on the class.
You can use clojure meta-data feature which allows meta data (a map) to be attached to any object that implements IMeta interface (which turns out to be every object as IObj extends IMeta and every object extend IObj)
Now there are 2 options.
1) You can attach the meta data to a var (the var points to the actual IFn object)
(defn hello {:name "hello"} [] 10)
and on Java side you get hold of the var hello and use IMeta methods to get the meta data and detect if your specific meta data is there or not.
The problem with this may be that your Java code access/handle IFn objects directly rather than their vars (ex: Anonymous functions), to solve this try the 2nd option.
2) Attach the meta data to the function object itself:
(def hello (with-meta (fn [] 10) {:name "hello"}))
You cannot use defn as that attach meta data to the var. The above sample code attach the meta data to the function object itself. On Java side, typecase the function object to IMeta and do the check.
The above code can be made a bit more defn likish with a help of a macro which is left as an exercise :)
It turns out that if you enclose the function body with a let statement containing a local definition, that variable name will appear in the method's local table in the class file. A bit cumbersome, though. Still looking for a better way.

How does clojure's defrecord method name resolution work?

After defining a record and the interfaces it implements, I can call its methods either by its name or using the java interop way using the dot operator.
user=> (defprotocol Eat (eat [this]))
Eat
user=> (defrecord animal [name] Eat (eat [this] "eating"))
user.animal
user=> (eat (animal. "bob"))
"eating"
user=> (.eat (animal. "bob"))
"eating"
user=>
Under the surface, what is going on there? Are there new clojure functions being defined? What happens when there are functions you defined that share the same name (is this possible?), how are these ambiguities resolved?
Also, is it possible to "import" java methods for other java objects so that you do not need the . operator so that behavior is like above? (For the purpose, for example, of unifying the user interface)
When you define a protocol, each of its methods are created as functions in your current namespaces. It follows that you can't have two protocols defining the same function in the same namespace. It also means that you can have them in separate namespaces and that a given type can extend both[1] of them without any nameclash because they are namespaced (in opposition to Java where a single class can't implement two interfaces with homonymous methods).
From a user perspective, protocol methods are no different from plain old non-polymorphic functions.
The fact that you can call a protocol method using interop is an implementation detail. The reason for that is that for each protocol, the Clojure compiler creates a corresponding backing interface. Later on when you define a new type with inline protocol extensions, then this type will implement these protocols' backing interfaces.
Consequently you can't use the interop form on an object for which the extension hasn't been provided inline:
(defrecord VacuumCleaner [brand model]
(extend-protocol Eat
VacuumCleaner
(eat [this] "eating legos and socks"))
(.eat (VaacumCleaner. "Dyson" "DC-20"))
; method not found exception
The compiler has special support for protocol functions so they are compiled as an instance check followed by a virtual method call, so when applicable (eat ...) will be as fast as (.eat ...).
To reply to "can one import java methods", you can wrap them in regular fns:
(def callme #(.callme %1 %2 %3))
(obviously you may need to add other arities to account for overloads and type hints to remove reflection)
[1] however you can't extend both inline (at least one of them must be in a extend-* form), because of an implementation limitation