kotlin-graphics/glm

static methods

rutenkolk opened this issue · 10 comments

Hi, I'm not sure if this is a by-product of Kotlin but is there a technical reason why the usual functions like glm::frustum are dependent on an Instance instead of just being a static method of the generated class?

This makes passing the functions around themselves somewhat tedious/infeasible as they need to be invoked on an instance of the glm class.

Specifically I am trying to call these methods from clojure where the usability is even more impacted by the methods not being static. I can get around this by writing a few macros, but I would rather not add another layer of indirection.

Hi!

Technically, the usual functions are dependent on an Kotlin object which is simply a singleton.

The main reason is to restrict the availability of all those functions on a single handler (glm indeed) rather than polluting the global namespace.

Also, static method inheritance is forbidden in Kotlin by design, because this has been a nightmare for Java users (you can read more here)

So, what we are doing is grouping those functions in interfaces and then making object glm implementing those.

This also allows users to create a custom handler implementing only the wished interfaces.

Kind like of C where you import only the headers you want.

Java users can simply

import static glm_.Java.glm

and they are good to go

I don't konw if this is valid also for Closure.. I have to admit I have always been intrigued by that language, althought I really never tried it for anything serious..

Would the java import work for you?

Can you show me some code snippet?

Thanks for the fast reply and explanation.

The main problem here is, that this makes functional programming somewhat hard. Java Methods are already somewhat uncomfortable to work with in clojure and needing an object to invoke a method on just adds to that. If Kotlin always relies on Singleton, I see no easy way out.

Since clojure is a lisp, the follwing might look funny. When using Java-interop and needing to invoke an instance method, instead of

myobj.methodName(arg1, arg2)

the syntax becomes:

(.methodName myobj arg1 arg2)

But most of the time you directly use functions as values instead of invoking them on an object (note the lack of a dot before the method name):
This mostly looks like something like this:

(clojure-function mistery-value mistery-value)

As you can see, the object on which the method is invoked on, is an explicit argument to the method.
It is common to pass around functions themselves in clojure. With native clojure function there is no problem and static java Methods can still be somewhat easily wrapped in clojure functions. With instance Methods on the other hand, you always need to keep track of the object the method needs to be invoked on and this problem escalates further.

therefore calling frustum with some extra calculations becomes a version with an "extra" argument for every method call:

(def glm-inst glm.glm/INSTANCE)
(.frustum glm-inst (.radians glm-inst 42.0) 21.0 42.0 21.0 42.0 21.0)

This led me to the question why there is even is a need for an Object at all.

If I see this correctly, glm_.Java.glm is an Object in the glm_.Java class, right? This unfortunately has the same problem for me.

(un/fortunately?) there is no static import or equivalent in clojure.

This hasn't held people (including me) back implementing their own hacks, which walk through java classes and re-define and wrap parts of said classes in a clojure namespace, but it's another layer of indirection and may break in unexpected ways if the underlying dependency updates.

To clarify, I imagine wrapping the glm methods so something along those lines will be possible:

(map glm/radians [90.0 45.0 0.0])

Which now looks like:

(map (fn [input] (.radians glm.glm/INSTANCE input)) [90.0 45.0 0.0])

To sum up: I don't think the java import is helpful for my usecase, but this issue is rather an annoyance rather than a deal-breaker. Seems like I will have to resolve to some lisp-macro-magic.

The main problem here is, that this makes functional programming somewhat hard

Not on Kotlin (and Java). I can easily val mat = Mat4 { c, r -> c * 2 + r }

If this is a problem in Clojure, we can try to find a solution for this language

The first thing that comes into my mind is to maybe declare a glm class for Clojure users with all the method as static?

Oh sorry I assumed Kotlin forbade static methods, going by your previous comment.

Yeah, since something likeinstance::fn as in

mystream.map(instance::fn)

is unavailabe in clojure, a class with static methods would be a great help!

The inheritance is forbidden. But you do can have static methods on classes.

Let's do a quick and simple test, I'll add a small class with one or a bunch of static methods this evening and you'll tell me how it goes on your side, ok?

here you go

449bdbc

Sorry, I only now got around to testing this.
I built the 1.3 branch and included the dep into a test project and it works just fine 👍

user=> (glm_.Clojure/frustum 1 2 3 4 5 6)
#object[glm_.mat4x4.Mat4 0x7b51564a "glm_.mat4x4.Mat4@3ee00000"]

Glad to read that :)

Now we shall write all the methods in there.. this is gonna take a while..

Would you be available to take care of that part? To begin, you may start adding only what you need..

Hi RutenkolkC,

I created a dedicated class Clojure for you where you can start playing/testing it

I'll send you also an invite shortly

Let me know :)

Closed for inactivity