Mark Needham

Thoughts on Software Development

F#: Stuff I get confused about

with 6 comments

Coming from the world of C# I’ve noticed that there are a couple of things that I sometimes get confused about when playing around with stuff in F# land.

Passing arguments to functions

The way that we pass arguments to functions seems to be a fairly constant cause of confusion at the moment especially when doing that as part of a chain of other expressions where the use of brackets starts to become necessary.

In C# I’m used to putting the arguments in parentheses but that doesn’t quite work in F#.

For example in my twitter application I was trying to append two lists together similar to this:

let first_item = Seq.singleton("mark")
let second_item = Seq.singleton "needham"
let joined_items = Seq.append (first_item, second_item)

Which doesn’t compile with the following error message:

The type 'b * 'c' is not compatible with the type 'seq<'a>'

What we’ve done here is pass in a tuple containing ‘first_item’ and ‘second_item’ instead of passing them separately as arguments to the function.

The correct way of doing this is like so:

let joined_items = Seq.append first_item second_item

Values and Expressions

As I understand it in everything that we create in F# is an expression and when those expressions get evaluated we end up with some values.

I wrote previously how we got confused about this distinction in a coding dojo a couple of weeks ago. That particular example was around how we need to create functions which take in an argument of type ‘unit’ if they are to be picked up by the XUnit.NET test runner.

Dave explains how this works in the comments of that post:

Given this code:

let should_do_something () = Assert.AreEqual(2,2)

The extra space implies that should_do_something is a function, which takes one argument which is a unit. This is more similar to the syntax for declaring a one argument function where the argument is actually a value, such as

let square_it x = x * x

When we put brackets around the arguments we are passing to functions they stop being passed as arguments as the compiler now tried to evaluate what’s in the brackets first and pass it to the function.

To give an example from playing around with Seq.append, if we do this:

let joined_items = Seq.append (first_item second_item)

We get a compilation error over ‘first_item’:

The value is not a function can cannot be applied

Here the compiler attempts to evaluate the function ‘first_item’ with an argument ‘second_item’ but since ‘first_item’ is actually a value and not a function this is impossible.

Referencing other types in our code

From my experiences so far it seems that F# uses one pass compilation such that you can only reference types or functions which have been defined either earlier in the file you’re currently in or appear in a file which is specified earlier in the compilation order.

This seems a bit restrictive to me although I’m sure there’s probably some benefits of this approach that I’m not yet aware of, maybe around type checking.

Be Sociable, Share!

Written by Mark Needham

May 2nd, 2009 at 2:38 pm

Posted in F#

Tagged with

  • Pingback: DotNetShoutout()

  • Larry Smith

    Just for clarification, in your point about Passing arguments to functions

    1) The culprit isn’t the parens, it’s the comma. A Tuple is defined as a comma-separated list of items. The parens are not specifically needed. With that said, the precedence of the “,” operator is fairly low, so the expression “foo a,b” is taken as a 2-tuple, the first part being the value of “foo a” and the second being “b”. However if you wrote it with parens, “foo (a, b)”, this applies the function “foo” to the 2-tuple “a, b”.

    2) Technically, when you call a .NET Framework function (“foo”) that takes (say) three arguments, the F# interface requires that you pass a *single* parameter, which in this case would be a 3-tuple. Hence the parens are required (as per the previous point). So the difference is that in F# you need the parens so that the statement parses the way you want it to, whereas in C# they’re necessary simply because that’s a syntactic requirement of the language.

    2a) I’ve never tried it, but presumably you could rewrite “foo(a, b, c)” as two statements, “let args = a, b, c” followed by “foo args” (and with no parens at all).

    Re your point about single pass compilation, I recently asked Don Syme about making multiple source passes to improve the type inference process. His reply was “Yes, it’s possible to do multi-pass type inference. There are also single-pass variations that generate a finite set of constraints.

    However these approaches tend to give bad error messages and poor intellisense results in a visual editor.

  • Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #340()

  • Pingback: Weekly Link Post 93 « Rhonda Tipton’s WebLog()

  • Pingback: Learning: Writing about simple things at Mark Needham()

  • Pingback: Mark Needham: Learning: Writing about simple things | Software Secret Weapons()