I decided that it was time to get into a new language. Being the unfocused, indecisive person that I am, I couldn’t decide between Clojure and F#, so I chose both. In a way, they are very similar. Both were made to interoperate with a mature, popular language inside of a VM while using an entirely different programming paradigm from its host language.
Clojure brought Lisp to the Java Virtual Machine, along with built-in Java interop. The promise of Clojure is that you get to write Lisp while reusing all the Java libraries and your existing Java code. F# has similar selling points: you get to write an OCaml-like language while at the same time reusing any C#/.NET library or code. While Clojure is mostly community-developed, F# has corporate sponsorship from Microsoft.
Both languages combine the object-oriented nature of their host platforms with their own approaches. F# is very functional in nature: you get immutable variables and data structures, first-class functions, lambdas, closures, currying, and partial function application. On the CLR, you can eliminate tail calls for recursion. The object oriented aspects are all present in F#, but they are treated differently than the ML-flavored features. You can declare a class in F#, but you can also create discriminated unions for pattern matching.
On the other hand, Clojure is a Lisp, so you can implement functional programming with a library, but it’s safe to say that Clojure is not functional in nature. Proponents of the language claim it’s functional (I believe several books mentioned this), but the power of Clojure is that it’s a Lisp with homoiconicity and syntactic macros. Clojure does have first-class functions and immutable variables and data structures. Neither F# or Clojure has the idea of pure functions (side-effect free functions).
To learn these languages, I did a lot of reading. When I was sick of reading and wanted to program, I decided to implement something simple: a CSV parser. There’s no standard for CSV (although there is an RFC), so I just chose/made up a definition that would fit most of the use-cases I’ve encountered.
A third language, for comparison
This may sound weird, but I decided to compare writing Clojure and F# against the exact opposite of these languages: C. It’s a language that is imperative, mature, has manual memory management along with pointers, doesn’t have strings, and doesn’t have a standard collections library. I realized that even with a small task like a CSV-parser, I was completely lost in how to organize my program and how to write clear, bug-free code. The program I wrote is terrible, but I’ve put it on GitHub regardless (the name is c-csv). The idea behind comparing F# and Clojure to C is that the former languages are very high-level with an emphasis on programmer productivity at the expense of CPU cycles and RAM whereas C makes the opposite tradeoffs.
It took me 7-8 hours as well as several pages of code to implement the parser in C. I love the terseness of the language, as well as the feeling of power when you create nearly all your tools from scratch, but I also disliked the fact I really didn’t have a choice.
F# brought some big guns to the language shootout: type safety along with type inference, pattern matching, and discriminated unions. It has tons of other features that I’d use if I were programming something larger, but for a simple parser, I only needed a few basic tools. Incredibly, I wrote a parser in less than 40 lines and in less than an hour. It’s available on GitHub: I called it fs-csv. I’m sure better programmers than I could outdo these stats, but I was blown away by the fact that even I could create a readable, incredibly short program that it fit easily on the screen.
I tried the same with Clojure, but I realized that I didn’t understand the language. I ran out of patience with trying to build a CSV parser, so I instead spent my time on Clojure Koans and 99 Lisp Problems. That was a worthwhile experience, but I still don’t feel as though I really know Clojure/Lisp. Lisp is a powerful tool: you can use it to create your own language that then solves your application’s problem, or at least that’s the idea. I realize this, yet I’m not to the point where I think in Lisp. Within a few hours, I could think in F# (partially because I’d used Haskell and partially because it’s just a sensical language). I’ve worked my way through the Little Schemers book, but I’m still not a Lisper.
Another part that I just don’t like with Clojure is that it’s not typed and it doesn’t compile fully at run time. You create an application that will then compile your Clojure code when you run it. It’s the speed of compiling combined with the safety of interpretation. The stack traces are bit confusing as well (since you’re calling into JVM-land which is not built as a Lisp-machine).
Higher level languages are vastly more productive
My end conclusions were several. First, high-level languages like F#, Python, C#, or Java 8 are definitely time savers. A language with managed memory, built-in strings, collections, and first-class functions is always going to beat out C when it comes to developer productivity. Given how little code you can use when building things in F# compared to C, you also win out for long-term productivity. It’s silly to think that once you build all your tools in C you can create your own high-level language and be just as productive as in C#. A modern, managed language with batteries-included core libraries will beat the pants of off C. This should be obvious, but this exercise made it painfully clear.
Second, F# is a great language that has tons of time-saving features. I really like C# (in comparison to Java), but I want type inference along with the functional aspects of Haskell. Speaking of that language, I do not miss having to think about having purity by default, or the issues with Cabal, or the lack of libraries. F# has access to all .NET libraries. Oh, and it’s easy to compile JVM code to .NET, so you get all JVM code too. No, seriously. I used the Java Stanford NLP library in a little Java project because I wanted to fiddle with it. I recently discovered that the Stanford team compiles the code to .NET as well since it’s really simple to do so with a tool called IKVM.NET. And they put their code on Nuget, the package manager for .NET. Let me say that again: the Stanford team produced a Java library that’s really awesome and .NET developers can use the same library without the Stanford team rewriting it in C# or F#. It’s like an infomercial: order F# now and you’ll get all the Java libraries thrown in too!
Third, Lisp is hard for me, and I assume it’s hard for a lot of programmers who come from a Java/Python/C# background. In these languages, you write a solution to your problem. In Lisp, you write code that will write a program that will solve your problem. This is the next step in how programming languages will work and it only makes sense. Every programmer, after typing up some program, will realize that they have written the same program or the same lines before. There’s a deep desire in all programmers to solve the problem once and never again. Constructs like functions, objects, generics, libraries, code-generation, lexical macros, and aspect-oriented programing are all means to further this end. While they approach the ultimate goal, you still have to write so much repetitious code. Lisp holds out the promise of programming your programs, of allowing you to create code that writes code for you.
Clojure is a Lisp, so it comes with the all positives and the negatives of that language. The core idea of Clojure is that you program your program instead of directly solving the problem. This added layer of abstraction comes with an incredible amount of power along with an additional toll on your brain. Learning to program is not easy, and programming a program is even harder. Lisp or a Lisp-like language might be the future of programming, but right now we have to build software teams with average programmers. Lisp is not the language of average programmers.
After using Clojure, I must report with a heavy heart that I don’t think there’s much hope that Java will be replaced. With Java 8 and the community libraries, you can get too much out of Java. Every project will probably use Guava, Apache Commons, and a hundred other libraries to patch the language deficiencies, but that’s better than the tradeoffs of all the other options.
In the end, I would love to use F# in a full-time job. I can’t say the same of Clojure, but I know that has more to do with me than the language itself. I can say that I would never use a Lisp if I wanted to hire people (unless I had a big budget in a big city). F#, on the other hand, would fit in well in a stodgy corporate setting as well as a nimble startup. I wouldn’t mind putting it into the hands of a junior programmer.