F# Ascendant
05 Dec 2014His name is indeed branded with the reproachful epithets of pirate and robber, to which the conquerors of every age are so justly entitled...
These days, it seems like F# is ascendant.
I have long considered OCaml one of the nicer languages I have worked with, though I have sadly not touched it in years. So when I had the opportunity and time for some exploratory research, I jumped at the chance to try out F#. The world needs more ML dialects!
Coding in F# was very comfortable, much like slipping into a warm bath. But I suspect the intervening years, and particularly deep explorations into Haskell, have changed my programming language expectations in ways I did not anticipate at first.
But let's talk about my first F# project for a bit.
Finger Trees
I chose to implement the finger trees data structure because I wanted to play around with F#'s typing system. Finger trees in particular require first-order nested types: at depth 1, Node<T>
, at depth 2, Node<Node<T>>
and so on. Furthermore, Node
s are parameterized by monoids, and thanks to some clever use of monoidic properties, this allows the finger tree to operate as a generic data structure.
As per the original paper, I implemented:
- a random access list
- a priority queue
- an ordered sequence
- and an interval tree.
All this code is on my Github repository.
(For a more practical implementation of finger trees, see Haskell's Data.Sequence.)
I had a tremendous amount of fun while working in F#, but a few things kept nagging at me.
What the...
Right off the bat, I missed Haskell's equivalent of type classes, which surprised me. In OCaml, functors and modules are roughly equivalent to type classes, modulo loss of some brevity. In F#, the only alternative seems to be a combination of object-oriented interfaces and dictionary passing, which seems unpalatable to me -- especially since this solution does not preserve type safety.
To be fair, there is indeed limited support for generic arithmetic (for example, via static constraints). Nonetheless, I found it awkward to deal with. It must also be admitted that OCaml is no better in this regard (c.f., the famous +.
floating point operator).
I also found myself annotating more than I thought I would have to. Perhaps this is due to the slightly unusual finger tree type structure, but it did feel like type inference was not as strong as it could have been.
Coming from Haskell, I also hit the dreaded "value restriction." Because values are prohibited from being generic, I found myself unable to eta-reduce to point-free functions. (You can certainly add monomorphic type annotations, but that rather defeats the purpose.)
Damn You, Haskell
While frustrating, the issues above are understandable because F# is constrained by the CLR (no type erasure), its ML heritage, and OO paradigms. I did not see any crippling defects in the language itself. So I do not begrudge F# these flaws.
Really, I would not have noticed most of these limitations even a few years ago. Up until now, I had not realized how pervasive and thorough Haskell was in terms of re-writing my programming language perspectives.
Leaving pernicious Haskell influences aside, F# is really a fine language in its own right.
The Delights of the Garden
I have covered my major annoyances, so it is only fair to talk about these features I was thrilled to find:
- Operator overloading. Yes, operator overloading can be abused. But it is so, so convenient. I missed this in OCaml.
- Units of measure. Aw, yes. I would love to see more of this in other languages.
- Active patterns. Haskell has the flexible ViewPatterns extension, but for some reason, I found active patterns in F# easier to use.
- Query expressions. LINQ in F#. Enough said.
I did not have enough time (there is never enough time, alas) to plumb deeper depths. I would have liked to explore F#'s inter-operability with other CLR languages, for example.
All that being said, my exploratory research was conclusive in at least one regard: I will be promoting and using F# for work (one of the few advantages of leading a startup!)
There is no doubt in my mind that F# is ready for prime time.
References
During my journey through F#, I was guided by a few hundred StackOverflow posts and a couple of books.
Expert F# Programming is a passable reference book, but it did not do much for me beyond elucidating some design decisions. But then again I am probably not in its target demographic. I can see, however, how it may be useful for newcomers to the ML world. The second book I consulted was F# Deep Dives, and though this book is still in preview mode, I can recommend it whole-heartedly: one of the few books about F# that focuses on practical, real-world usage.