I’ve been gradually coming to the conclusion that I’d rather use LINQ than F#, both because it’s more pleasant and because it better fits what I want from a functional language.
There are all sorts of factors that contribute to this, large and small. Some examples:
- F#’s laziness is difficult to use, and must be explicitly declared
- I end up wrestling with F# over types, even in situations where Haskell, my gold standard for a fiercely-typed language, can cope. For instance, one cannot add an integer to a BigInt, or compare them for size.
- “;;”. At the coders’ social, I had this discussion with the two friends I’d recruited to work with me:
- me: “Interpreter lines end with semicolon-semicolon”
- them: “What are they using semicolon for?”
- me: “List separation”
- them: “Then what are they using comma for?”
- me: “Tuple separation”
- them: “But… semicolon semicolon? Why?”
- me: *hopeless shrug*
- Recursive functions must be recursively declared, making them the
marked state.
- The built-in List library doesn’t contain half the functions I look
for, like take, drop, and their higher-order cousins: takeWhile and
dropWhile.
- Casually printing temporary results is annoying, requiring both
“printfn” (“print” doesn’t appear to be taken) and strict typecasting.
- List.zip requires lists of the same length. (This and non-laziness being the default mean that my most common zip-using scenarios don’t work in F#.)
And, of course, the lovely example I just spent five minutes on:
List.zip [1, 2, 3] [3, 4, 5];;
val it : ((int * int * int) * (int * int * int)) list
= [((1, 2, 3), (3, 4, 5))]
I’d expected a list of pairs.
The problem turned out to be, ironically, too much implicit behavior: [1, 2, 3] is interpreted as [(1, 2, 3)]. I’d much rather have the implicit behavior that lets me compute 12345678I > 0.