Getting started with F#

Updated 13th November: Robert pickering has answered the questions. I’ve included his answers inline. Thanks Robert!

The talks I went to at TechEd today (Friday) were mostly related to concurrency and F#. I’ve decided that although it’s fairly unlikely I’ll use it in a serious manner, it’s worth learning F# mostly to become more comfortable with functional programming in general. Apart from anything else, this is likely to help in terms of LINQ and general concurrency.

I was fortunate enough to win a copy of Robert Pickering’s “Foundations of F#” today at the final talk, answering a “pop quiz” question set by Joe Duffy. (Whoever claims that there’s no point in a C# developer knowing details of the CLR memory model is thus proven wrong in a most bizarre way.) I’ll probably buy Don Syme’s “Expert F#” when it comes out. I’ve been the Foundations book on the plane, where I’m currently hunched over my laptop, desperately hoping it doesn’t run out of power before we land. My situation has one important impact on this post: I haven’t actually written any F# yet. As it happens, with the VS2008 release being pretty imminent, I’ll probably wait until that’s out before installing F#.

In a way, my complete inexperience with the language is a good thing, for the sake of what I’m writing here: these are my raw impressions of F# based solely on what I’ve read. Now, some of the points I’m going to make here are potentially about the book instead of F# – I want to make it perfectly clear that I’m not trying to criticize the book. In general, it’s been an easy read so far (I’ve read about 70 pages) and my main problem with it is that there are typos. Normally that’s fine, but there are some cases where I don’t know whether I’m missing something about F# or whether there’s just a typo. Errors like this are to be expected, however hard we try to avoid them. I know that I’m still finding typos in chapters of C# in Depth that I’ve read several times, and I’m sure there will be some in the finished product. Anyway, with that disclaimer, along with the reiteration that these are just first impressions, here are my thoughts so far. They’re numbered for the sake of ease of reference, and as I find out answers to any questions, I’ll include them at the end of the point in italics.

  1. Byte literal strings – what encoding is used? This feels like a bad idea, although I’m sure it’s useful sometimes. (At least the normal strings are plain .NET strings; as I understand it IronRuby uses non-Unicode strings internally by default and only converts to/from Unicode when calling .NET code. The things we do in the name of compatibility…)

    Robert: It doesn’t say what encode is used in the language specification, however I strongly supect its UTF-8 as the spec says that unicode characters are allowed but “A”B comes out as [|65uy|]. I think they were added to help out Ocaml users who are used to having mutable strings thus providing an easier migration for ocaml programs which take advantage of mutable strings (although having non-mutable strings generally feels like a good design choice for an FP).
  2. #light is used at the start of every listing , and Robert explains that he’s not going to explain the non-light syntax. I know this is only a single book, but isn’t that at least a good indiciation that perhaps it should be the default syntax style?

    Robert: The light syntax really isn’t that different from the non-light syntax, it just means you can miss out certainty tokens such as “in” and semi-colon (;) when the context is clear from the whitespacing. This small change does however make listings look a lot neater and forces you to make whitespacing reflect the structure of the program (which is a very good thing). The choice not to explain it was made for two reasons 1) I though explaining would be more confusing that just telling beginners not to worry about it 2) it provides beginners very clear guidance to what syntax they should be using. Should the F# compiler be light by default? I can see the advantages, but adds a little extra complexity for people trying to do F#/Ocaml cross compilation.
  3. List comprehensions: why do I need [] round the range when declaring a value, but not for iteration? I can do “for i in 1..5″ but not “let x = 1..5″. Isn’t this inconsistent?

    Robert: This is because the surrounding bracket types denote the type of collection [] is list [||] is array and {} is Seq/IEnumerable, this is directly copied from creating a literal list of these types. Also you can’t use say “for i in 1..5” you need to say “for i in do …” which you can also do in list comprehension [for i in 1..5 -> (i, i*i) ] for a list of tuples of squares.
  4. The “when” clause in a for loop – I’m sure it’s to look like OCaml, but coming at a time when the non-functional world is used to “where”, it’s unfortunate. I’m not saying that F# has made the wrong decision here – it’s just a pain when two different histories collide.

    Robert: No comment :)
  5. P28 has ‘let objList = [box 1; box 2.0; box "three"]‘. The box operator here isn’t fully explained as far as I can see – but the main interesting idea is that a string could be boxed. In “classic” .NET boxing refers to creating a reference type instance out of a value type value – but string is already a reference type. What’s going on here?

    Robert: box is not just for boxing structs, it also performs an upcast to type object. A result of F#’s type inference is that there is no implicit upcasts as there are in C#. F# provides the box keyword and :> operator to compensate for this

    Jon: That seems pretty unfortunate to me, when box already has a clearly defined meaning in .NET. I suspect this will confuse quite a few people.

  6. P37 looks like it’s using a C-style pre-decrement: ‘| x -> luc (x – 1) + luc (–x – 2)’ – is that –x just a typo for x?

    Robert: –x is just a strange and embarrassing typo (already listed in the errata)
  7. Another possible typo: P41, first listing the final line is ‘| [] -> ()’ where in the previous example the result had been [] instead of (). That makes more sense to me, as otherwise the function returns unit where otherwise it’s returning a list. Typo, or am I missing something?

    Robert: This is correct, in the other rules of this pattern matching we’re printing to the console, which has type unit as a result, so we need to use unit for the final rule as well.

    Jon: Oops. Doh!
  8. Union/record types: as a C# guy, my natural question is how these things look in IL. Is a union just a name and a value, and if so is it in a struct or a class? Will have to dig out reflector when I’ve got the compiler…

    Robert: Record types are classes with properties for the each of the fields. The union type is represented as class with inner classes to represent each of the cases. The outer class provides methods and properties for working with each of the cases from C#. There is a good description of what a union type looks like in the final chapter of the book.
  9. There are quotes in the output at the bottom of P48, from calling print_any: ‘”one”, “two”, “three”, “four”‘ – are these genuinely in the output? I haven’t found a specific description of print_any yet, but I don’t think we’ve seen quotes round all strings. I could be wrong though :)

    Robert: print_any tries to reconstruct its input into its literal equivalent, so strings come out quoted listed look like ["one"; "two"; "three"]

    Jon: Ah, handy. A bit like Groovy’s inspect() method.

  10. Another terminology clash: “raise” for exceptions rather than “throw” is likely to confusion me for a while; “raise” sounds like an event to my C#-tuned ears.

    Robert: Again, no comment
  11. Supposedly OCaml is really efficient when it comes to exceptions. Now, I usually find that when people talk about exceptions being expensive in .NET they’re basing that on experience under the debugger. Are OCaml exceptions really significantly faster than under .NET? It’s far from impossible, but I’d be interested to know.

    Robert: I believe OCaml exceptions are a lot faster that .NET exceptions, but then they don’t carry any of the debugging information that .NET exceptions do. It is reasonably common to use exceptions as follow control in OCaml which is a bit of a no no in .NET
  12. Why is “lazy” a keyword but “force” isn’t? It feels odd for part of a feature to be in the language but its other side (which is necessary, as far as I can see) being in the library instead.

    Robert: I believe lazy is a keyword because it helps tidy up the precedence when you are creating lazy values. It maps directly to a function in the F# libraries that can be used instead. I believe this is a design decision inherited from OCaml.
  13. “unit” is an interesting name for what I’d normally think of as “void” – I wonder what the history is here? “void” isn’t as descriptive as it might be, but “unit” is even less obvious…

    Robert: Wikipedia has some more info here: http://en.wikipedia.org/wiki/Unit_type but is doesn’t cover the history of the name.
  14. It took me a little while to get the hang of () really being like void instead of like null – so () as a function return is the equivalent of “return;” which requires a void return type – it’s not “return null;”. Nothing in the book suggests that it is null – that was my own misunderstanding, and I don’t know what caused it.

    Robert: No comment :)
  15. P59 talks about mutable record types, and states that “this operation [updating a record] changes the contents of the record’s field rather than changing the record itself”. Now, I suspect the difference being alluded to is the same as “changing a value within a reference type instance’s data is not the same as making a variable refer to a different instance” – but if it’s not, I don’t know what it is meant to be saying.

    Robert: Yes, you are change the value with the reference
  16. P67/68: Do while and for loops require a “done” terminator or not? The text claims they do, but the examples don’t include it. My guess is that the language specification changed while the book was being written, and that the examples are correct, but I’d appreciate clarification :)

    Robert: They only require done when there’s no #light declaration and yes #light declarations were added half way though writing the book
  17. P69: I assume “for x in y” works where y is any sequence (i.e. anything implementing IEnumerable)?

    Robert: Yes, its works for anything that implements IEnumerable<T> or IEnumerable.
  18. P70: Calling methods and specifying parameter names – in the example, the parameter names are actually in the same order as they’re declared in the method itself. Is this always the case? Can you reorder the use of the parameters? Note that (at least in C#) reordering could have an effect on what the eventual arguments were, if the evaluation of the arguments has side effects. I’d really like to see this feature in C# though – it would save on all those comments explaining which parameter method means what!

    Robert: Just tested:
    System.Console.WriteLine(arg = [| box 1 |], format = “{0}”)
    And it compiles okay, so named arguments can be reordered.

    Jon: Ooh, I’ve got feature envy :)

  19. Why can’t I use a .NET method as a value, just as I can use an F# function value? If the method were overloaded I might have to provide some type information to the compiler to tell it which overload I mean, but otherwise I should be able to use it just like anything else without wrapping it. I assume there’s a deep implementation reason why this is impossible, but it seems a bit of a shame.

    Robert: Actually you can now, it’s another case of the language spec changing while [the book was being written]

    Jon: Cool. Always good to hear news like that.

  20. P73: Indexers aren’t always called Item – that’s just the default name for the default indexer. You can have other named ones, although C# will only use whichever has the appropriate attribute applied. (Even in C# you can change the name emitted when you declare an indexer though.)

    Robert: I’ve just read sections 10.9 of C# 3.0 specification and I see no reference to attributes or the ability to change the indexer name. Also section 10.3.9.3 “Member names reserved for indexers” seems to suggest that Item is the only reserved name. What am I missing?

    Jon: The attribute in question is System.Reflection.DefaultMemberAttribute – but it appears that you can’t apply it in C# when there’s already an indexer, contrary to my previous belief. However, if you use IL which specifies a DefaultMemberAttribute other than Item, it works fine.
  21. Also on P73, there’s this line: ‘let temp = new ResizeArray<string>() in’ – is the ‘in’ part here a typo, or is it another bit of syntax that I’ve missed somewhere?

    Robert: “in” is optional here because of the #light syntax
  22. What’s the :> operator shown  on P77?

    Update: P82 explains that it’s the upcasting operator
  23. What is ‘try … match’ referred to on P77?

    Robert: This is try … match is the equivalent of try … catch
  24. Is box actually a keyword or not? It’s not listed in the list of keywords, but it looks like it should be…

    Robert: box is a function not an keyword, its defined in Microsoft.Fsharp.Core.Operators if you want to see its definition (prim-types.fs).

I don’t intend to make notes as I read the rest of the book – it takes too long. However, I hope any F# evangelists find these initial reactions useful to some extent.

2 thoughts on “Getting started with F#”

  1. The Unit type seems to have a good history in functional programming and type theory, although I first encountered it in Scala. It’s not quite the same as void, since it’s a real type with only one value – presumably (). The difference is more obvious when it’s a template parameter than when it’s a return value.

Comments are closed.