Crying out loud: things I dislike in F#

Alex Yakunin
6 min readSep 21, 2016

Ok, I decided that I have to write about this :) I really, really love F# — it’s an amazing language. It fits well for both simple and complex problems — that’s something that pretty rare (e.g. IMO it’s definitely better than Python in this sense). Besides that, it’s probably the fastest functional language you can find (at least if you run F# code on .NET, not Mono) — it’s very close to C# in terms of performance. But…

If you want a comparison, F# is like a sports car with many small dents. They don’t affect on anything from technical standpoint — their only purpose is to annoy the owner :) Worse, the car is so nice that these small dents annoy you even more, because they contrast so much with the rest. And if you are a perfectionist (luckily, I am), you simply can’t stop paying attention to these dents.

So don’t get me wrong: I love F#, and if you’d ask me to list what’s good there, I’d probably produce a much longer list than this one. But people don’t cry about good things. Thus I’m going to talk about things I really dislike, or simply hate :)

A bit of context: I am using C# from ~ 2003 — this is my primary language. But I am playing with F# for a few years, and I use it only for relatively small problems so far. It’s fair to claim that I have a very solid experience with C#, and some — with F#.

What I dislike in F#:

  1. You have to manually order your files in F# projects in order they must be processed by the compiler. I.e. basically, if file2.fs depends on file1.fs, file1.fs should go first, and file2.fs should be below it in the list of file. This is simply awful. Ok, most of F# projects contain relatively small number of files (partially, because there is an agreement that files must correspond to modules, i.e. they typically contain many types and functions), but this still looks like a very ancient requirement.
  2. No break and continue for loops. Maybe I’m wrong, but I don’t see a single reason of why they aren’t there, esp. assuming that any loop expression is always a unit expression. And yeah, it’s really annoying: you can workaround the absence of these keywords by e.g. adding an extra mutable variable + converting the loop to “while”, or rewriting the loop with tail recursive function. But the main question is: why I should take care of this, but not the F# compiler? And don’t tell me that these keywords must not exist in functional languages: the beauty of F# is that it’s both functional and imperative. It’s totally unwise to tell me that I can both drive and walk, but if I have to walk, I can use only one leg.
  3. No do-while loops. Again, not a big deal, but since most of developers are used to this, what’s the point of not adding this feature?
  4. Any tuple type is a reference type in F#. Performance wise it’s an extremely bad decision. Value tuples are already on the way, but IMO it’s much better if your tuples are value types by default (and as far as I understand, this won’t be the case). And on contrary, C# tuples will be struct tuples by default — wise choice (after a big mistake with Tuple type — it’s also reference type in C#).
  5. Multiple meanings of the same keyword. It seems F# developers were very savvy with keywords — and frankly, I don’t get why. There is well-known single responsibility principle, and I totally don’t get why many language developers like to break it so much. Let’s look on a few examples:
    a) “let” can be used to declare locals, but also private fields. But wait, there “val” as well, that also allows to declare private fields. And that’s not it: “val” also allows to declare public properties, and if you declare a property, you should write “member val Name” (note: no “this.”), but if you declare a method, you should write “member this.Name(…)” :) Do you understand the logic behind all these bindings? I don’t.
    b) “do” is used in “for”, “while”, and also to declare type constructor. But there is also “new” keyword for secondary constructors. Wait, what — two keywords for constructors? Yes, F# developers didn’t save a keyword here :)
    c) “type” keyword allows to create any kind of type — discriminated union, type alias, record, struct, and class. If you think about type keyword itself, it’s not bad. Bad side is: unless you completely get used to its syntax, it’s pretty hard to remember what’s the right way to declare any of these constructions: you must use “= { … }” for structs, “(constructor params) =” for classes, “= struct” for structs, “= | A | B” for discriminated unions and simply “= A” for type aliases. Ah, and there is “and type” for mutually dependent types. Common, guys, do you really need this to be so explicit?
    d) “=” means “bind a with b” in “let” syntax and many other constructions, but also “equals to” in expressions. Why, why you hate “==” so much? And yeah, “not equals to” is “<>” in F#, not “!=” — it really really wants to be different to a majority of languages.
    e-z) I can find more examples of this, I guess. But let’s move on :)
  6. Awful type constructor syntax. I guess long story short is: someone decided that it might be fun to treat almost the whole type body as a pseudo-constructor: you can declare and initialize fields, properties and constructor locals there, but if you need some extra code, you should add “do” section. Worse, if you want to have one other constructor, you declare it in a completely different way. Ah, and there are static constructors declared with “static do”. All of this makes an impression that someone decided that “more is more here, implicit is better than implicit, so let’s be very flexible here”. As a result, it’s really flexible, but you need days to truly remember what are all these non-straightforward rules. Plus it’s very different to most of other languages. IMO this just proves that less is more (but except for do-while, break, and continue :), and explicit syntax is typically better too.
  7. “Many ways to do X” vs “only one way to do X”: in general, there is only one way to do something in F#, but there are exceptions. The most awful examples I remember are:
    a) array type can be declared as “int[]” or “int array”; same for list and seq; moreover, it’s the same for any other generic type with one type argument (e.g. you can write “float Vector”, which is the same as “Vector<float>”. So… Is this really so important to support a completely different syntax here?
    b) Arbitrary “this” in member declaration syntax. Personally I don’t see any value in allowing to use any identifier there instead of a single one. As a result, you frequently see “__” is used instead of “this”, typically to indicate that “this” is unused in the member body. And btw, you can’t use “_” as a placeholder for unused argument here, though it works in any other place :)
  8. “fun x y z -> …” syntax for lambda expressions. If you guys are so concerned about the cleanness of syntax, why there is seemingly excessive “fun” part? Even JavaScript managed to eliminate “function”— this is almost like getting rid of “a” in English.
  9. “collection.[index]” syntax for indexing (i.e. “dot+[“ syntax). Ok, “[…]” is reserved for list construction. But I don’t see why it’s so problematic to identify which case you actually have — e.g. like this, maybe:
    a) <expression>[expression>] = <indexingExpression>
    b) <expression><spaces>[<expression>] = <expression> <listConstructionExpression>
    Please, please implement this.
  10. Only explicit type conversions. I understand that this is probably the price you have to pay for such an awesome type inference, but still, I don’t like this :) E.g. if you use F# for contest problems, you constantly have to explicitly cast between int, int64, and float.
  11. [| array |], [<attribute>] syntax, and in general, heavy use of special symbols. E.g. for arrays I’d simply prefer “array [ … ]”, which would make it even more similar to seq / list expressions (+ there is anyway “array” keyword). That’s because I am typing “[||]” slower than “array []”, + there is a higher chance I’ll make mistake while typing “[||]”, though may be it’s just me, and most of other people love pressing / releasing shift on every second keystroke.
  12. “<-” for assignment. Another personal thing, maybe, but “:=” seems easier to type. Though as I wrote above, “=” for both “bind” and “assign” + “==” for “equals to” would be way, way better. But it’s too late to cry about this, I guess.

That’s it for now. Hopefully, some of these issues will be addressed in future versions of F#, though as I said, most of this is simply annoying / inconsistent. It shouldn’t stop anyone from using this awesome language :)

--

--