We have working code.
I have tagged a recent commit to give a snapshot of the code at this point. Given the possibility that some decisions will be revisited and some of this code revised, this snapshot preserves this first cut.
The code here is all F#. Mostly it is ported from the C# ClojureCLR implementation, but with an eye to writing idiomatic F#. Given how it will be used, I had do some things one might avoid in a closed F# implementation. For example, given how
null) is used in Clojure, I could not think of how to avoid a liberal sprinkling of the
[<AllowNullLiteral>] attribute on interfaces and classes. Tuple arguments are used often instead of curried functions. Because of overloading, some groups of functions that could have been gathered in a module are in a static (sealed abstract) class. When I understand the code better, I’ll revisit some of this. But I wanted to make some progress now.
For those with some familiarity with the existing Clojure implementations, know that I have restructured the code substantially. There are multiple projects. I have tried to use namespaces appropriately This will have consequences when it comes to doing the Clojure interop that you see in
core.clj and company, but given that these internals are to be accessed by Clojure users through the
clojure.core environment, this should remain
This first snapshot has the following functionality.
Clojure.BigArith– this project (and namespace) provides
BigDecimal- implements an immutable, arbitrary precision, signed decimal. This implementation was inspired by the General Decimal Arithmetic Specification. I did not implement the entire specification. For one thing, it is big and hairy. For another, I really only needed to miimic the API of
java.math.BigDecimal, as that is all Clojure uses. My implementation is close to the X3.274 subset of the GDAS, meaning I do not implement infinite values, NaNs, subnormal values, and negative zero, along with few other things. I’d have to go read a book to find out what a subnormal value is. Maybe someday.
My C# implementation of
BigDecimalwas based on my own implementation of a
BigIntegerclass. Back in the day, I didn’t find a usable
BigIntegerimplemenetation, so I just built my own. I based this new implementation of
System.Numerics.BigInteger. I didn’t not bother porting my
Rational– Not used in Clojoure. Clojure has its own, rather limited,
clojure.lang.Ratio. Mine is cooler, but no matter; I just wrote this for fun.
Murmur3- An implementation of the Murmuer3 hashing API. Clojure on the JVM imported the Guava Murmur3 implementation and made some changes. I copied the API and based the algorithms on the description on Wikipedia. See also SMHasher. Murmur3 provides the mechanics for our
Ratio- see above. This is minor. It doesn’t even implement its own arithmetic, relying on
Converters- I pulled the code used to convert
Objects to a numeric type (when possible) from
clojure.lang.Utilto this project. Makes more sense here, and I can use it here. This is part of the deconstruction of the massive
RTclasses in order to decouple types.
Numbers– the big enchilada. Described in the previous posts. Big. I may have a few more unit tests to write. But there are over 400 tests now. I’m feeling pretty good about it.
Hashing– I moved the utility code supporting hashing from
Clojure.Collections– The start of the implementation of all the Clojure collections. In this snapshot:
The interfaces – you’ve been introduced to
ISeq, but they are joined by essentially all the other interfaces that are used (I counted 32 more.)
AFn– an abstract base class to help with defining
IFnderivatives. I’ll post about this some other time. I needed it early on to help test
IPersistentCollection. Those tests are not in this snapshot. A post for another day.
SeqEnumerator– provides enumerators (= Java iterator) for
ASeqneeds this and it can be implemented without any dependencies, other than on the interface
The core types to enable us to properly define the many sequential data structures to come:
As described in a previous post, these are mutually dependent.
Enough bits and pieces of
Utilto get us going. Only a vestigial ipmlementation of
RT.seq, as discussed. Enough to get started, but I don’t have the extension mechanism defined yet. That will have to be soon, as we will be adding to its repertoire soon.
LazySeq–I had a version of it from 18 months ago when I first started playing with this stuff, so I just tossed it in.) Soon we’ll need to have a little chat about laziness, chunking, and reducing.
There are test suites written in Expecto for each of the projects. They are executables. You can just run them.
Some random notes on the code text
Naming of functions and methods is a complete shambles, as it is for the C# code for ClojureCLR today. From the beginning of that project I was faced with reconciling the different capitalization standards of Java and C#. I ended up with a mixture. I kept some names with the initial lowercase just so I would have to do less rewriting the Clojure source; other places, I went with intial uppercase. It’s a mess. I regret it.
And this code is a mess, too. And I don’t care. For now. I don’t wan I nt to think about it at this point. I’ll have a big renaming party eventually.
Coming a little to my own defense, the situation in F# is a little confused, too. There are coding guidelines for F# that are pretty clear, at least when the project is F# only. Other rules come into play if you are creating a library for outside consumption, from C# say. Obviously, there are no rules for writinng F# for Clojure interop.
When I’ve written enough code, I’ll have a better sense of the rules I will abide by. In the meantime, just know that I know. Don’t judge.
At this point, it should be pretty easy to march through many the sequential data structures that rely on
ASeq. That includes
StringSeq. I may do some of these just take the occasional break from our next big adventure: maps.