There’s been a distinct lack of interesting language related posts around here recently.
Why? Well because I’ve been mostly writing Ruby and C. Neither of which languages fills me with an urge to post HEY GUYS, I FOUND THIS COOL THING. Ruby because I vaguely dislike it, so I resent even the kindof cool things I find as I use it, C because my use of C tends towards the extremely straightforward so C posts tend to be about the result rather than the method (arguably that’s what programming posts should be like anyway, but that’s a separate issue).
But anyway, I’ve been window shopping around for a new language to learn. My list of features I wanted in said language were:
- Good performance
- Higher level than C
- Preferably statically typed
- Generic collections
- Didn’t run on the JVM
The closest to this on the list of languages I already knew was Haskell, and I’ve been using it a little bit, but it and I don’t really get on for a variety of reasons that are beyond the scope of this blog post.
I was pretty much thinking it was going to be O’Caml (which is another language in the “vaguely dislike” category. I want to like it, I really do, but somehow I can never manage) and C++ (oh dear god no).
So, anyway, my ears perked up when clay came up on reddit the other day. I had already been vaguely aware of it, but somehow it dropped off my radar before I did anything about it.
So I’ve been spending today playing with it, and the results are two little projects: An interval tree implementation and a very rudimentary start on some client bindings to PostgreSQL. Neither are written with any particular purpose in mind, ‘though both are something I might want to use at some point, it’s really just a way to get a feel for the language.
I’m pleasantly impressed. Is it going to be the Next Big Language? Who knows. Certainly not me. But despite a lot of stupid questions on my part it was a pleasant experience, and I’m definitely going to continue playing with it.
What follows are some initial impressions. Bear in mind it’s the result of only a day of playing with it, so they may be incomplete or outright wrong:
Here are some of the things I liked:
Good support for generic programming
Like it says on the label really. This is one of the things the language bills itself as being good at, so if it weren’t it would be dead on launch.
I’ve had a reasonable play around with it, and it works well. The entire system is basically a giant (mostly) statically resolved multimethod system. Given my fondness for multimethods, that’s a plus as far as I’m concerned. Runtime polymorphism comes in through an extensible variant system: You can declare variants in one module and add new ones in another.
Anyway, the result is that there are generic collections and they Just Work, exceptions make use of the extensible variant system and seem well behaved.
There’s no object orientation support and no subtyping. As far as I’m concerned, that’s a good thing.
Decent FP primitives
So I haven’t actually had much of a play with these yet believe it or not, but they’re there and seem to work. lambdas don’t implement non-local return (‘though I suppose you could do this with an exception), but do capture their environment. The main place I’ve used this is in the test system.
clay bind-gen takes a C header file and gives you a clay library. It’s a bit finicky, and obviously the code is far from idiomatic, but it does work and works well. It took me about 10 minutes to get this working on the libpq header and port a C example of using it to clay. Hopefully this means that it’s rather easy to build on the available C ecosystem.
Decentish standard library
All quite basic at the moment, but it already annoys me less than Scala’s used to when it was at a much later stage in the game than this. Granted that’s because Scala could get away with it by falling back on the JVM ecosystem and clay can’t, but it’s a good start. It’s pleasantly reminiscent of the sort you’d expect with a high level language, but seems well tuned for clay’s suitability as a systems language.
Valgrind works flawlessly
As per title. Clay is an unsafe language – it has manual memory management (which, thanks to things like stack allocation and deterministic destructors, is much less of a pain than in C, but is still definitely not hard to get wrong), pointer arithmetic, no bounds checking on array access.
The only reason I trust myself to write C is because valgrind exists. I would have had a very hard time trusting myself to write clay if it didn’t work on it. Fortunately, no problems.
Friendly and intelligent IRC channel
I’m a big fan of IRC as a learning environment. I’ve been on IRC on a fairly regular basis in various channels for 10-15 years (‘though rarely any given one for more than 4 or 5. I have community attention span issues), and so the first thing I do when checking out a new tech or language is long onto their IRC channel. Clay’s does not disappoint. It’s small, but there are a bunch of smart people in there, and they were very patient and helpful with my stupid questions.
Generally there’s a good reason for it, and things are still in flux so I think many of these will improve, but there have been a bunch of cases where I’ve gone “Wait, what?”. Some of these are just the result of my never having used a language with non-trivial value semantics before (tree = tree.child generates an invalid read), some of them are gotchas which you’ll probably only be caught by once but are still pretty ugly (it’s very easy to think you’ve redefined a constructor when you haven’t because the type arguments are part of the constructor’s name), and a few others that slip my mind at the moment. The semantics of how copying, moving and assignment work are not so much confusing as very easy to get wrong. I’m still not 100% sure I understand how variants interact with their members.
I think I’m largely on top of it now, and I expect it will become clearer as I write more of it, but I definitely spent a good chunk of today quite bewildered.
Basically zero documentation
Well, what do you expect? The language is young and still massively in flux. I’m not surprised there’s no documentation and, as mentioned, the community is very helpful in making up for its lack, but it definitely makes the learning process much harder.
The language is young and still massively in flux
As mentioned above. This is definitely an experimental language – the compiler seems to currently be undergoing a rewrite which will, amongst other things, be changing the backend target away from LLVM and to C. The syntax is expected to change. The semantics are expected to change. The language designers will, and should, feel free to break backwards compatibility.
As well as the usual problems with new languages and quality of error messages, clay’s error messages, both at runtime and compile time, are very short on line numbers. This can make it very hard to track down what’s going wrong. It wasn’t drastically difficult with what I was doing today, but they were both rather small projects. I don’t know how hard it will be on larger projects, but I’m hoping that some of this (particularly compile time error messages) will be fixed by the time I have to worry about that.
Late discovery of errors
It’s not quite as bad as C++ template errors are reputed to be, but generally you don’t discover an error in a function until you try to use that function. This is somewhat deliberate, as it ties in with how the genericity works, but when combined with the line numbers problem can be quite frustrating. Additionally the support for giving the compiler hints to get better error messages is a bit rudimentary: You can define compile time functions which constrain arguments, but that’s about it.
Keywords vs operators
Clay doesn’t have much C compatibility in terms of its operators: the bitwise operators (in particular <<, >>, |, & and ^) are replaced by functions. The shortcutting boolean operators are replaces with keywords and and or. It’s not a major problem, but it annoyed me vaguely when porting from C.
Clay has a fair few keywords: ref, forward, lvalue, rvalue, lambda, and, or, overload, procedure, variant, record, callbyname… probably more.
It’s not a massive problem, but I tend to regard keyword surplus as a sign of insufficiently well factored features. I’ve yet to form an opinion on whether or not this is the case here.
Everything is by reference
I don’t yet know if I like this or not, but Clay’s calling convention passes everything as a mutable reference. So assigning to a function argument assigns in the calling context. Really. This can be quite useful, particularly given that copying a clay value can be a very expensive operation, but I find it deeply disconcerting. I feel like it may grow on me. We’ll see.
A few minor syntax weirdnesses
e.g. one that bugged me earlier is the syntax for return type declaration on a function: foo(a : Blargh) ReturnType. No colon on the return type, but colon on the arguments.
Overall, definitely more good than bad. I intend to keep playing with it and see how I feel after a while doing so.