I've been trying to get into F# on and off for a while but I keep getting put off. Why?

Because no matter which 'beginners' resource I try to look at I see very simple examples that start using the operator ->.

However, nowhere have I found as yet that provides a clear simple explanation of what this operator means. It's as though it must be so obvious that it doesn't need explanation even to complete newbies.

Actually Methuselah lived for 969 years so I would still be a third of his age. However, edited thank you ;)
–
AnthonyWJonesSep 20 '08 at 20:01

4

If the unit of age used was actually the lunar month we would have Methuselah dying at the ripe old age of 75 - which is entirely credible, and certainly noteworthy in a desert nomad.
–
Peter WoneJan 22 '10 at 23:05

9 Answers
9

'->' is not an operator. It appears in the F# syntax in a number of places, and its meaning depends on how it is used as part of a larger construct.

Inside a type, '->' describes function types as people have described above. For example

let f : int -> int = ...

says that 'f' is a function that takes an int and returns an int.

Inside a lambda ("thing that starts with 'fun' keyword"), '->' is syntax that separates the arguments from the body. For example

fun x y -> x + y + 1

is an expression that defines a two argument function with the given implementation.

Inside a "match" construct, '->' is syntax that separates patterns from the code that should run if the pattern is matched. For example, in

match someList with
| [] -> 0
| h::t -> 1

the stuff to the left of each '->' are patterns, and the stuff on the right is what happens if the pattern on the left was matched.

The difficulty in understanding may be rooted in the faulty assumption that '->' is "an operator" with a single meaning. An analogy might be "." in C#, if you have never seen any code before, and try to analyze the "." operator based on looking at "obj.Method" and "3.14" and "System.Collections", you may get very confused, because the symbol has different meanings in different contexts. Once you know enough of the language to recognize these contexts, however, things become clear.

I've accepted this as the answer because its the closet to a the sort of complete answer I was looking for but there are a number of other details from other answers that help. I'm unconviced that -> is not an operator however. Can anyone else confirm this?
–
AnthonyWJonesSep 20 '08 at 20:39

2

You could say it's a type-level operator. Rather, a datatype constructor. ( a -> b ) is isomorphic to (a, b). The arrow in the former is the same kind of thing as the comma in the latter.
–
ApocalispSep 20 '08 at 21:26

Apocalisp, it took a couple of readings (you're assumption that I knew waht isomorphic means held me back) but I get it now.
–
AnthonyWJonesSep 21 '08 at 11:22

1

And now in a for loop it stands for do yield ([for i in 0..n -> i]).
–
CogwheelFeb 20 '10 at 19:09

First question - are you familiar with lambda expressions in C#? If so the -> in F# is the same as the => in C# (I think you read it 'goes to').

The -> operator can also be found in the context of pattern matching

match x with
| 1 -> dosomething
| _ -> dosomethingelse

I'm not sure if this is also a lambda expression, or something else, but I guess the 'goes to' still holds.

Maybe what you are really referring to is the F# parser's 'cryptic' responses:

> let add a b = a + b
val add: int -> int -> int

This means (as most of the examples explain) that add is a 'val' that takes two ints and returns an int. To me this was totally opaque to start with. I mean, how do I know that add isn't a val that takes one int and returns two ints?

Well, the thing is that in a sense, it does. If I give add just one int, I get back an (int -> int):

> let inc = add 1
val inc: int -> int

This (currying) is one of the things that makes F# so sexy, for me.

For helpful info on F#, I have found that blogs are FAR more useful that any of the official 'documentation': Here are some names to check out

(a -> b) means "function from a to b". In type annotation, it denotes a function type. For example, f : (int -> String) means that f refers to a function that takes an integer and returns a string. It is also used as a contstructor of such values, as in

val f : (int -> int) = fun n -> n * 2

which creates a value which is a function from some number n to that same number multiplied by two.

Function types are the types given to
first-class function values and are
written int -> int. They are similar
to .NET delegate types, except they
aren't given names. All F# function
identifiers can be used as first-class
function values, and anonymous
function values can be created using
the (fun ... -> ...) expression form.

The nice thing about languages such as Haskell (it's very similar in F#, but I don't know the exact syntax -- this should help you understand ->, though) is that you can apply only parts of the argument, to create curried functions:

adder n x y = n + x + y

In other words: "give me three things, and I'll add them together". When you throw numbers at it, the compiler will infer the types of n x and y. Say you write

adder 1 2 3

The type of 1, 2 and 3 is Int. Therefore:

adder :: Int -> Int -> Int -> Int

That is, give me three integers, and I will become an integer, eventually, or the same thing as saying:

five :: Int
five = 5

But, here's the nice part! Try this:

add5 = adder 5

As you remember, adder takes an int, an int, an int, and gives you back an int. However, that is not the entire truth, as you'll see shortly. In fact, add5 will have this type:

add5 :: Int -> Int -> Int

It will be as if you have "peeled off" of the integers (the left-most), and glued it directly to the function. Looking closer at the function signature, we notice that the -> are right-associative, i.e.:

addder :: Int -> (Int -> (Int -> Int))

This should make it quite clear: when you give adder the first integer, it'll evaluate to whatever's to the right of the first arrow, or:

add5andtwomore :: Int -> (Int -> Int)
add5andtwomore = adder 5

Now you can use add5andtwomore instead of "adder 5". This way, you can apply another integer to get (say) "add5and7andonemore":

add5and7andonemore :: Int -> Int
add5and7andonemore = adder 5 7

As you see, add5and7andonemore wants exactly another argument, and when you give it one, it will suddenly become an integer!

Many great answers to this questions, thanks people. I'd like to put here an editable answer that brings things together.

For those familiar with C# understanding -> being the same as => lamba expression is a good first step. This usage is :-

fun x y -> x + y + 1

Can be understood as the equivalent to:-

(x, y) => x + y + 1;

However its clear that -> has a more fundemental meaning which stems from concept that a function that takes two parameters such as the above can be reduced (is that the correct term?) to a series of functions only taking one parameter.

Hence when the above is described in like this:-

Int -> Int -> Int

It really helped to know that -> is right associative hence the above can be considered:-

Int -> (Int -> Int)

Aha! We have a function that takes Int and returns (Int -> Int) (a curried function?).

The explaination that -> can also appear as part of type definiton also helped. (Int -> Int) is the type of any of function which takes an Int and returns an Int.

Also helpful is the -> appears in other syntax such as matching but there it doesn't have the same meaning? Is that correct? I'm not sure it is. I suspect it has the same meaning but I don't have the vocabulary to express that yet.

Note the purpose of this answer is not to spawn further answers but to be collaboratively edited by you people to create a more definitive answer. Utlimately it would be good that all the uncertainies and fluf (such as this paragraph) be removed and better examples added. Lets try keep this answer as accessible to the uninitiated as possible.