I’ve been playing with F# on and off for about one year, but only recently I was able to complete a few “real world” projects. I was so impressed that I decided to share my experience. In this two-part series I will talk about two very different projects to give you an idea of how wide the spectrum of applications is where F# feels right at home.
The first project
The first project is named VeloSizer. You can check it out here (I may release it as open source but I’m still undecided on what to do with it). I assume you are not a cycling geek so I’ll spare you the details, but in short this application computes the bike setup given your position and the frame geometry. If you’re interested there’s a detailed description on the application page. Surprisingly enough, I’ve never found anything that does this very thing (except for full blown and expensive CADs), so I decided to write it myself.
The application is built in Silverlight: the XAML frontend is basically a glorified input form. It’s not particularly complex, some details are more complicated than it may look at first sight, but still there’s nothing extraordinary. I took a rather standard approach and employed the MVVM pattern (using MVVM-Light) for a clear separation of concerns. The View Model is C# while the Model –where the interesting stuff happens– is written in F#.
Solving this particular problem does not require very complicated mathematics, but involves a large number of geometrical operations (trigonometry and the likes). Without abstracting and hiding away all the math, the solution quickly becomes a nightmare that spirals out of control (don’t ask how I know). For this reason I’ve implemented a simple 2D CAD engine that sits at the application core.
How it went
Here are some things I noticed while using F# in a “real” project for the first time.
Units of measure
F#’s support for units of measures built straight into the type system has been very helpful to avoid stupid errors like mixing degrees with radians with millimeters, etc. It is really a plus when dealing with physical dimensions.
Conciseness
The language syntax is very light and unobtrusive, which makes it ideal to write mathematically-oriented code. The main benefit to me has been that the math stands out clearly, without parenthesis, type annotations or artifacts that make things harder to read. Also writing the code is a joy: you can really focus on the reasoning and almost forget that you are actually programming. In fact translating the equations written on paper to code is almost copy & paste.
Testability
I heartily agree with Richard Minerich when he says that testing does not replace a strong, theoretically-validated model. It’s the very same reason that pushed me to build most of this application’s engine on paper before writing a line of code. However I still make (lots of) mistakes when implementing a model –regardless of how correct it is– so I feel safer with the additional support of a solid testing framework.
The nature of functional programming makes it an ideal target for unit tests. Short, side-effects free functions are a joy to test. Result: it has been very easy to create a nice safety net in form of an NUnit project.
I must admit I would probably have written this library more or less using the same style in C#, but in functional programming this is the default.
Interoperatibily with C#/GUI
This is somewhat of a sore point. I don’t know if it is due to my lack of experience (likely) or the nature of a GUI-driven application, but I’ve ended up with many mutable (and not very idiomatic in general) classes, for two main reasons:
- I had to persist the business objects (using the Sterling NoSQL database) and all the serializers for Silverlight need public setters as they are not allowed to use reflection.
- With MVVM, each View is bound to its respective View Model, which is nothing more than a wrapper around its respective business object defined in the model (F#).
Now when for instance the user changes a value in a TextBox, the new value is propagated to the View Model, which in turns propagates it to the Model. You can tell it’s not very practical to create a new instance of the model every time a value changes, so immutable objects do not adapt so well to the situation.
This means that the business objects are very C#-like. They still benefit from the lighter syntax, type inference, etc…, but they don’t fully leverage the power of the language. Fortunately the “application brain” does not suffer much from this.
Is this due to MVVM, XAML and in general GUI patterns being oriented towards the object-oriented paradigm? I don’t know. I’ve heard of a GUI framework specifically written for F#, but I don’t know much more.
I would be very interested to hear your opinion on this subject.
Note: as Stephen points out, keeping the model immutable may not be so much of a problem. I’ll give it a try.
Guidance and community support
The F# community is still small, but it more than makes up for it in quality. The active users on Stackoverflow and other sites are extremely competent. It’s rare to get bogus answers or to get stuck on a problem for long.
What I’ve found difficult though is getting guidance. I often ask myself if my code is well written or a pile of junk. I suppose the only solution is to refine my own sense by reading other people’s code.
Intellisense
Visual Studio’s Intellisense for C# is spectacular and has made us very lazy. F# support is much better than it was at the beginning, but it’s still not up to the same level of C#. In the end though it’s only lacking a few details like parameter names or support for the pipeline operator –the next release already includes some improvements in this area.
Debugging
Setting breakpoints and watching state change is not simple in functional code because (usually) there is no state. If you debug a lot, this may be a bit unsettling at first, but then you realize it is not so much of a drawback. It is a benefit in fact. Breakpoints are evil: building a half-working solution, running it through breakpoints and tune it until the result matches what you expect is very close to the definitions of cargo-cult programming/programming by coincidence.
It is my opinion that functional programming makes you think more and write/edit/debug less. I believe this has made me a better developer because I now tend to stop, think about the solution “offline” and only write it down when I get it.
Productivity
I can’t give any judgment on productivity because this application has been a pet project I’ve built alone without any deadline, working literally 15 minutes at a time. We recently welcomed another family member, which has made things even harder. Anyways it took me about 7 months to complete this project, but it’s very hard for me to tell if F# has given any productivity boost at all. More on this in part two.
Conclusion
It has been a real pleasure to write the F# part of this application. When you look at the application source, the first things that jumps to the eye is that the View Model (C#/OO) is way larger (in lines of code) than the model (F#/mostly functional), yet it only does “stupid” things: it’s almost exclusively made of property definitions, RaisePropertyChanged events, brackets, etc. It is like a very large box full of bubble wrap sheet, with only a small, precious gift in the middle.
That said I’ve been left with the impression that I haven’t used all of the language’s power. Writing the View Model in F# would only have slightly alleviated its ineffectiveness, what I need is probably a different pattern for GUI interaction.
In part two I’ll talk about a very different (and more interesting) project, where F# really shined. In the mean time I would be very interested to hear your opinions.
Thanks a lot to Steffen Forkmann and Samuel Bosch for proof reading and general feedback!
Hi Francseco.
Thanks for the blog post.
I’ve played with VeloSizer. Nice.
I agree with all of your points.
Conciseness –I chose F# (when still a research project) for it’s promise re: “… translating the equations written on paper to code is almost copy & paste. …” Refolding Planar Polygons is a Moph I use for my art project. No panacea for my math skills, but smooths my cogitation process.
Interoperatibily with C#/GUI: I revisited this sore point recently. Ouch. I too found good Guidance and community support. There seems to be a lot of recent activity and interesting work by Phil Trelford, Matt Podwysocki, Tomas Petricek, Fahad Suhaib, and Don Syme’s blog posts too… Phil’s tweets — Reactive programming is v. productive for GUI apps. Seen many C# coders using Rx lately for WPF/SL/WinForms. for F# I’d recommend a combination of async workflows, agents + IObservable (+/- Rx) for GUI. With C# expect same after C# 5.
I’d been noodling with Jon Harrop F#.Net Journal Computational Geometry: Quick Hull article, interested in the algorithm and the data structure for polygon. With Jon’s help I explored a TPL parallel too; F#UN.
I thought — I’d like to add points to the pre-gen’d polygon points (how hard can it be?), just a little GUI front-end interactive MouseMove code …
I really like “drag” :
“A key aspect of the GUI is the ability to drag and drop points in the point set and watch the convex hull as it is recomputed in real time. The following drag value is the current state of the outstanding drag operation, if any, represented by the index of the point being dragged:
> let drag : int option ref = ref None;;
val drag : int option ref = {contents = null;}
This design follows the mantra “make illegal states unrepresentable”. Alternative designs in languages where this cannot be so easily expressed tend to use one boolean variable to indicate whether or not a drag is in progress and another integer variable to convey the index when meaningful. Using data structures such as the option type in F# precludes the possibility of non-sensical values being created and encountered at run-time, reducing the scope for errors and the number of run-time tests. …”
But I show my lack of functional sophistication when I tried to grook “canvas.MouseMove.Add(move canvas update)”. I think my mind got stuck on the C# side of GUI interface with F# …
I was looking for (object sender, MouseEventArgs e) … where “…the code move canvas update is actually a function call that returns a function, which is then added as a handler to the MouseMove event. …”
val move : IInputElement -> (int -> unit) -> Input.MouseEventArgs -> unit
I just got this insight from: http://stackoverflow.com/questions/8978297/f-wpf-mousemove-params
Best, Art
Excellent post and application. I especially wanted to comment on this:
“Breakpoints are evil: building a half-working solution, running it through breakpoints and tune it until the result matches what you expect is very close to the definitions of cargo-cult programming/programming by coincidence.”
I recently ported a sizeable C# project to F# and then to Scala. During the F# port, I continued using the VS debugger pretty much as I always had for C# — that is, as you describe above — as a coding tool.
For the Scala version, I was using different IDEs, with less familiar debuggers. The available debuggers were perfectly fine, but my unfamiliarity with their user intefaces caused me to rely more on the old-fashioned tools of paying more attention to the code, crafting-in trace points, etc.
At first it was frustrating, but then I noticed something interesting: I didn’t really miss not using a debugger as a coding tool. I re-discovered the fact that I could reason perfectly well about the correctness of the code just by using my brain and experience. Even if I needed to add things like trace points, I was adding them deliberately, as a result of first reasoning about the program, and not as a semi-random probe.
This is not to disparage debuggers, they are still a very, very useful and necessary tool when there is really is a tough bug to be squashed. But they can become a crutch when used in support of trial and error programming.
Which dovetails nicely with another observation I’ve made about F# and Scala: if you can get the IDE warnings to go away, the program nearly always runs correctly. That is, the functional programming aspects of F# and Scala simplify the reasoning about a program to such an extent that most errors are reduced to mere syntax.
GUI & func prog: you could have a look at http://dotnetslackers.com/articles/net/Programming-user-interfaces-using-f-sharp-workflows.aspx