F# syntax in 60 seconds

Here is a very quick overview on how to read F# code for newcomers unfamiliar with the syntax.

It is obviously not very detailed but should be enough so that you can read and get the gist of the upcoming examples in this series. Don't worry if you don't understand all of it, as I will give more detailed explanations when we get to the actual code examples.

The two major differences between F# syntax and a standard C-like syntax are:

Curly braces are not used to delimit blocks of code. Instead, indentation is used (Python is similar this way).

Whitespace is used to separate parameters rather than commas.

Some people find the F# syntax off-putting. If you are one of them, consider this quote:

"Optimising your notation to not confuse people in the first 10 minutes of seeing it but to hinder readability ever after is a really bad mistake."
(David MacIver, via a post about Scala syntax).

Personally, I think that the F# syntax is very clear and straightforward when you get used to it. In many ways, it is simpler than the C# syntax, with fewer keywords and special cases.

The example code below is a simple F# script that demonstrates most of the concepts that you need on a regular basis.

I would encourage you to test this code interactively and play with it a bit! Either:

Type this into a F# script file (with .fsx extension)
and send it to the interactive window. See the "installing and using F#" page for details.

Alternatively, try running this code in the interactive window. Remember to always use ;; at the end to tell
the interpreter that you are done entering and ready to evaluate.

// single line comments use a double slash(* multi line comments use (* . . . *) pair-end of multi line comment- *)// ======== "Variables" (but not really) ==========// The "let" keyword defines an (immutable) valueletmyInt=5letmyFloat=3.14letmyString="hello"//note that no types needed// ======== Lists ============lettwoToFive=[2;3;4;5]// Square brackets create a list with// semicolon delimiters.letoneToFive=1::twoToFive// :: creates list with new 1st element// The result is [1;2;3;4;5]letzeroToFive=[0;1]@twoToFive// @ concats two lists// IMPORTANT: commas are never used as delimiters, only semicolons!// ======== Functions ========// The "let" keyword also defines a named function.letsquarex=x*x// Note that no parens are used.square3// Now run the function. Again, no parens.letaddxy=x+y// don't use add (x,y)! It means something// completely different.add23// Now run the function.// to define a multiline function, just use indents. No semicolons needed.letevenslist=letisEvenx=x%2=0// Define "isEven" as a sub functionList.filterisEvenlist// List.filter is a library function// with two parameters: a boolean function// and a list to work onevensoneToFive// Now run the function// You can use parens to clarify precedence. In this example,// do "map" first, with two args, then do "sum" on the result.// Without the parens, "List.map" would be passed as an arg to List.sumletsumOfSquaresTo100=List.sum(List.mapsquare[1..100])// You can pipe the output of one operation to the next using "|>"// Here is the same sumOfSquares function written using pipesletsumOfSquaresTo100piped=[1..100]|>List.mapsquare|>List.sum// "square" was defined earlier// you can define lambdas (anonymous functions) using the "fun" keywordletsumOfSquaresTo100withFun=[1..100]|>List.map(funx->x*x)|>List.sum// In F# there is no "return" keyword. A function always// returns the value of the last expression used.// ======== Pattern Matching ========// Match..with.. is a supercharged case/switch statement.letsimplePatternMatch=letx="a"matchxwith|"a"->printfn"x is a"|"b"->printfn"x is b"|_->printfn"x is something else"// underscore matches anything// Some(..) and None are roughly analogous to Nullable wrappersletvalidValue=Some(99)letinvalidValue=None// In this example, match..with matches the "Some" and the "None",// and also unpacks the value in the "Some" at the same time.letoptionPatternMatchinput=matchinputwith|Somei->printfn"input is an int=%d"i|None->printfn"input is missing"optionPatternMatchvalidValueoptionPatternMatchinvalidValue// ========= Complex Data Types =========//tuples are quick 'n easy anonymous typeslettwoTuple=1,2letthreeTuple="a",2,true//record types have named fieldstypePerson={First:string;Last:string}letperson1={First="john";Last="Doe"}//union types have choicestypeTemp=|DegreesCoffloat|DegreesFoffloatlettemp=DegreesF98.6//types can be combined recursively in complex waystypeEmployee=|WorkerofPerson|ManagerofEmployeelistletjdoe={First="John";Last="Doe"}letworker=Workerjdoe// ========= Printing =========// The printf/printfn functions are similar to the// Console.Write/WriteLine functions in C#.printfn"Printing an int %i, a float %f, a bool %b"12.0trueprintfn"A string %s, and something generic %A""hello"[1;2;3;4]// all complex types have pretty printing built inprintfn"twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A"twoTupleperson1tempworker// There are also sprintf/sprintfn functions for formatting data// into a string, similar to String.Format.

And with that, let's start by comparing some simple F# code with the equivalent C# code.