Functions versus member methods in F#
Tagged as F#F# is a hybrid object-functional language, and allows you to write code in member methods (like C# methods) or in global functions. The F# library contains several cases where a member and a global function do the same thing. For example:
> let l = [ 1; 2; 3 ];; val l : int list = [1; 2; 3] > l.Length;; val it : int = 3 > List.length l;; val it : int = 3
Several other List module functions, such as head and tail, are also duplicated by properties. It’s not just lists, either: for example, the .NET String.Length property is duplicated by the F# library String.length module function.
So when would you choose a member such as .Length member over a function such as List.length or String.length, or vice versa?
The answer, perhaps surprisingly, is that you should usually choose the function. The reason is to do with F# type inference.
In F# code, you don’t usually need to specify the types of variables and parameters, because the compiler will work them out by analysing the code. Take a look at the following code fragment:
let twiceLength a = 2 * List.length a
F# infers that twiceLength takes a list and returns an int. We’ve not had to specify this: F# has worked it out. How?
Well, when F# tries to work out the type of a, it looks at how a is used in the body of the function. It sees that a is passed to the List.length function. Now it looks at the List.length function and sees that its argument is of list type. So, F# reasons, a must be of list type. Job done!
But what if we wrote the function using a member?
let twiceLength a = 2 * a.Length
Oh no! We get a compiler error, “Lookup on object of indeterminate type based on information prior to this program point.” What does this mean? It means that F# can only figure out that a must be something with a .Length member. And that’s not enough to pin down the type. List has a .Length member, String has a .Length member, ExperimentalGermanFilm has a .Length member… F# can’t tell which of these is intended, so automatic type inference fails, and we have to go back to writing it out longhand:
let twiceLength (a : String) = 2 * a.Length // Now F# can resolve the .Length call
In terms of the amount of code, there isn’t much to choose between the two. And of course, if you’re working with a type that only comes with members, not helper functions, then you’ll have to go the type annotation route — not that there’s anything wrong with that. But type inference is a bit more idiomatic where you have a choice.
And that, my liege, is how we know the earth to be banana shaped.
Leave a Reply
Categories
BrainDump (1)
Community Code (4)
Events (16)
F# (14)
General (53)
Lab Samples (2)
LightSpeed (268)
MegaPack (8)
News (71)
NHibernate Designer (26)
Nightly news (52)
Phone Elements (24)
Products (87)
Projects (5)
Screencast (6)
SharePoint (3)
Silverlight (14)
Silverlight Elements (66)
SimpleDB Management Tools (20)
Visual Studio (9)
VS File Explorer (7)
Web Workbench (39)
WPF (44)
WPF Diagrams (57)
WPF Elements (110)
WPF Property Grid (32)



Posted by Ivan Towlson on 27 February 2011 


