I've been interested in contributing to Clojure/ClojureScript for a while, although this was far from how I imagined the first patch I proposed looking like.
I'm not a Java dev by any means so this is just something that came up in the context of a very small numerical library I've been working on (
https://github.com/Sophia-Gold/Symbolic-Algebra.clj) that required me to dig into both core.clj and clojure.lang.Numbers and I had an idea that a variation of what I implemented could be of several potential uses when it comes to further dev work by others. So please, feedback is welcome as is taking this with a grain of salt.
Essentially my problem was that building a library of numeric types (so far rationals, complex numbers, polynomials, and combinations + subtypes thereof) using protocols eventualy came down to dispatching on a base type that could be cast across all of Clojure's numeric types. You can see how I solved that problem below by using gen-class to extend the java.lang.Number abstract class and then providing a constructor that used Clojure's (num) function to cast it based on reader input:
(ns symbolic-algebra.core
(gen-class
:name Number
:extends java.lang.Number
:constructors {[ ][ ]}))
(defn -Number [this]
(num this))
(defprotocol Algebra
(add [a b])
(sub [a b])
(mul [a b])
(div [a b])
(equal? [a b]))
(extend-type Number
Algebra
(add [a b]
(if (= (class a) (class b))
(+ a b)
(raise-types a b add)))
(sub [a b]
(if (= (class a) (class b))
(- a b)
(raise-types a b sub)))
(mul [a b]
(if (= (class a) (class b))
(* a b)
(raise-types a b mul)))
(div [a b]
(if (= (class a) (class b))
(/ a b)
(raise-types a b div)))
(equal? [a b]
(if (= (class a) (class b))
(= a b)
(raise-types a b equal?))))
Now, having combed over the math section of clojure/core.clj I obviously saw a lot of repetitive calls to clojure.lang.Number where (aside from implementing some functions directly) it essentially does the same thing I was doing except in Java code. So I've been sitting around for a couple days weighing the upsides and downsides of this seven line patch simply to add a constructor for clojure.lang.Numbers as I did in my project for java.lang.Number so as not to make a fool of myself in my first post on the dev list.
As I see it the considerations are as follows:
- Shouldn't break anything (again not a Java coder...so please correct me if I'm wrong).
- Likely to be confusing unless someone rewrites all the core math functions to use it (I would volunteer to do this, but I doubt anyone would want me to considering it would contribute no real value and require a rather non-trivial code review).
- Eases implementation of new core math functions...although it seems highly unlikely to me any are slated to be added any time soon.
- To the extent anyone is thinking about Clojure-in-Clojure for 2.0 or any further releases, this could be seen as an incremental step towards that or even a way to try it out in an incremental release by switching just the math functions over from the Java implementation to protocols. But obviously that's a much greater design decision than just a seven line patch...
- More along my interests: the constructor is now exposed for devs writing numerical libraries. As I've shown above, the (num) function is only for casting and can't be used as a the basis of an actual type system. I'm not sure if this is something that people have already solved in other ways, maybe with core.typed, but for me one of the benefits of using a dynamic language means being able to write expressive systems on top of one numerical type that handles casting for me.
Based on that, here's my proposed patch:
(ns ^{:doc "The core Clojure language."
:author "Rich Hickey"}
clojure.core
(gen-class
:name "Number"
:extends clojure.lang.Numbers
:constructors {[ ][ ]}
:prefix "clojure-"))
...
(defn clojure-Number
[this] (cast this))
As mentioned, all feedback welcome. I'm certain I can't be correct on all of the assumptions I've made here, so please, have at it. I've found this community incredibly welcoming, which is the only reason I feel comfortable coming out of nowhere and proposing a patch on the core language like this :)
Thanks,
Sophia
(As an aside: I think a lot of people write off Clojure's prospects for numerical computing since it's a hosted language with dynamic typing, but I've recently been blown away by how powerful it is in many applications and that's what's motivated me to do work like this and hopefully see more of it from others, which is what I believe a constructor for clojure.lang.Numbers would help enable. I can't say this particularly library was fun to work on due to the OOP structure, but I've viewing it more as scaffolding on which to play with some of the transducers I've been writing. Particularly, I'm beginning to port the lazy power series code from Haskell (Doug McIlroy), Scheme (SICP), Go (Rob Pike), etc.— all of whom I believe got the idea from research by Gilles Kahn. Based on speed tests on similar code, I have strong reason to believe the Clojure version will be by far the fastest.)