Call function of protocol masquerading as generic superclass/interface? - clojure

E.g.,
(defprotocol P
(foo [x])
(bar [x]))
(extend-protocol P
Superclass ;; a java abstract class
(foo [x]
(println "superclass"))
Subclass ;; a concrete java class implementing the above abstract class
(foo [x]
(foo (cast Superclass x))))
If calling
(foo subclass-instance)
I would get a stack overflow obviously, but is there some way to accomplish what I'm trying to do here, i.e., call the same function but masquerade as the generic superclass/interface?
Update: A clearer example demonstrating a use case for what I'm asking:
(defprotocol P
(extract-properties-into-map [self]))
(extend-protocol P
PropertyContainer ;; abstract class
(extract-properties-into-map
[this]
(into {} (for [[k v] (.getProperties this)] [(keyword k) (read-string v)])))
Foo
(extract-properties-into-map
[this]
(assoc {:properties (extract-properties-into-map
^PropertyContainer this)} ;; this is where it falls apart
:foo-type (.getFooType this)
:foo-permissions (.getPermissions this)
:foo-whatever (.getWhatever this))))

The problem with cast is that it works like a type assertion, just throwing an exception if your object does not satisfy the is-a relationship.
(defn cast
"Throws a ClassCastException if x is not a c, else returns x."
{:added "1.0"
:static true}
[^Class c x]
(. c (cast x)))
There is no new interface being returned to dispatch in a different function, i.e. you have a stack overflow.
I'm not sure what it means to be extending a protocol for an Interface? Since you're providing an implementation I guess you should define a type first and extend the protocol on that super-type.

EDIT: A slightly better answer based on delegating-proxy from https://gist.github.com/michalmarczyk/1715851
(defprotocol P
(foo [x])
(bar [x]))
(extend-protocol P
Number ;; a java abstract class
(foo [x]
(println "superclass:" x))
Integer ;; a concrete java class implementing the above abstract class
(foo [x]
(foo (delegating-proxy x [Number] []))))
Called with
(foo (Integer. 1))
=> superclass: 1
While it does as the question asked and it now wraps the original x. Depending on the requirements, it might be better to delegate foo to a function not included in the protocol, maybe superfoo
(defn superfoo [x] { :byte (.byteValue x) })
(defprotocol P
(foo [x])
(bar [x]))
(extend-protocol P
Number ;; a java abstract class
(foo [x]
(superfoo x))
Integer ;; a concrete java class implementing the above abstract class
(foo [x]
(merge (superfoo x) { :f (.floatValue x)})))
I think the underlying issue is Protocols do not know about class inheritance. Also, seems like Clojure should have a wait to coerce an object to a type. Type hints do not work in this case.

Related

Instance variable assigned in initializer wrongly inferred as nilable

Using Crystal 0.25.1 the type of the instance variable #foo in this example is inferred to be nil-able even though it can never be nil:
class Foo
end
class Bar
def initialize(foo : Foo? = nil)
#foo = foo || Foo.new
end
end
p typeof(Bar.new.#foo) # => (Foo | Nil)
I know that I can declare #foo : Foo at the class level, to work around the issue, but why can't the Crystal compiler infer that #foo can never be nil on its own?

Why scala List could take covariant type as paramenter in method +=

Scala List is declared as
sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqOptimized[A, List[A]] with java.io.Serializable
The method to prepend an element to a List is declared as
def +:(elem: A): List[A]
As type A is covariant, why the compiler does not complain since A appears in the contravariant position in +:?
Because its full signature is:
def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
The doc what you mentioned in the question is just the simplified one, you need to check the full signature of the method.

clojure: can I define an implicit conversion possility?

I have a protocol called IExample and I define a record type A that implements it:
(defprotocol IExample
(foo [this] "do something")
(bar [this] "do something else"))
(defrecord A [field1 field2]
IExample
(foo [this]
(+ field1 field2))
(bar [this]
(- field1 field2)))
Let's say I want to extend another (basic) type B to implement this protocol, but I know how to convert from B to A:
(defn B-to-A
"converts a B object to an A object"
[Bobj] ...)
because I have this conversion, I can delegate all calls of the IExample protocol
on a B to the IExample protocol on an A by delegating them:
(extend B
IExample {
:foo (fn [this] (foo (B-to-A this)))
:bar (fn [this] (bar (B-to-A this)))})
This, however, seems as an awful lot of boilerplate (especially for bigger protocols)
that is not clojure-idiomatic.
How can I tell clojure just to implicitly convert B to A every time
an IExample function is called on a B object, using the B-to-A function?
As far as the boilerplate is concerned, you can write some macro to write all that boilerplate for you. On the other hand, you could have a second look at your design here.
What we have here is 3 things (types): A, B and IExample. And then we have 2 relationships between these things: 1) a-to-example : A -> IExample 2) b-to-a : B -> A and from this we can get 3rd relationship by using composition i.e compose b-to-a with a-to-example : B -> IExample. Now if we try to move this design to protocols we will find that it is not a simple translation because protocols won't directly compose as discussed in the above design, instead we can use an intermediate protocol IToExample like shown below:
(defprotocol IExample
(foo [this] "do something")
(bar [this] "do something else"))
(defprotocol IToExample
(to-example [this] "convert to IExample"))
(defrecord A [field1 field2]
IExample
(foo [this]
(+ field1 field2))
(bar [this]
(- field1 field2))
IToExample
(to-example [this] this))
(deftype B [])
(defn b-to-a [b] (A. ....))
(extend B
IToExample {:to-example b-to-a})
What we did that we represented the -> IExample in our design as the IToExample protocol with one function. So we got:
a-to-example : A -> IExample by implementing IToExample for A
b-to-a : B -> A by a normal function
compose b-to-a with a-to-example : B -> IExample by implementing IToExample for B and using b-to-a.
It depends. If you look at the clojure core seq functions, you may notice that the ISec interface is only 4 methods, and that the whole "public" sequence library is defined by (many more) functions that call (some-internal-function (seq argument)) - and they tend to be explicitly documented as doing that too. Conceptually, there's an protocol like your IExample interface, and an additional protocol that describes the seq function to convert from some type to something implementing ISeq.
This is an especially useful strategy if the datatype only needs to implement a couple of methods (so IExample can be small) and the number of algorithms acting on the protocol is large (since you can write all of those in terms of regular functions).

How to map method name X to method name Y in Scala like in List

Referring to the answer of the following question:
List.empty vs. List() vs. new List()
How did the developers of Scala map the method apply[A](xs: A*) defined in the List object, to be usable as List[A](cs: A*)?
Also how did they translate aListInstance(n: Int) to the method apply(n: Int) (which returns the n'th element of the list) defined in the List class?
In both cases I'm calling the apply() methods without writing .apply() in my code. How does that work?
It works because the Scala Language Specification says so.
foo(bar)
is translated to
foo.apply(bar)
just like
foo.bar = baz
is translated to
foo.bar_=(baz)
and
foo(bar) = baz
is translated to
foo.update(bar, baz)
and
foo bar baz
is translated to
foo.bar(baz)
and
foo bar_: baz
is translated to
baz.bar_:(foo)
and so on and so forth.

How do I use Clojure memfn with a Java constructor?

I want to use a Java constructor as a first-class Clojure function. My use-case is to transform a sequence of strings into a sequence of Java objects that have a constructor with a single string:
Simple Java object:
public class Foo {
public Foo(String aString){
// initialize the Foo object from aString
}
}
And in Clojure I want to do this:
(defn make-foo (memfn Foo. a-string))
(apply make-foo '("one" "two" "shoe"))
The apply should return a list of Foo objects created from Strings, but I'm getting this:
IllegalArgumentException No matching method found: org.apache.hadoop.io.Text. for class java.lang.String clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
Don't bother. memfn is practically deprecated in favor of the anonymous function literal, with which you can also invoke constructors, e.g., #(Foo. %).
Also, your apply call is going to try to invoke make-foo once with three string args. You probably instead want:
(map #(Foo. %) ["one" "two" "three"])