F# syntax: indentation and verbosity
Understanding the offside rule
The syntax for F# is mostly straightforward. But there are a few rules that you should understand if you want to avoid common indentation errors. If you are familiar with a language like Python that also is whitespace sensitive, be aware that that the rules for indentation in F# are subtly different.
Indentation and the "offside" rule ##
In soccer, the offside rule says that in some situations, a player cannot be "ahead" of the ball when they should be behind or level with it. The "offside line" is the line the player must not cross. F# uses the same term to describe the line at which indentation must start. As with soccer, the trick to avoiding a penalty is to know where the line is and not get ahead of it.
Generally, once an offside line has been set, all the expressions must align with the line.
Various tokens can trigger new offside lines to be created. For example, when the F# sees the "=
" used in a let expression, a new offside line is created at the position of the very next symbol or word encountered.
Other tokens have the same behavior, including parentheses, "then
", "else
", "try
", "finally
" and "do
", and "->
" in match clauses.
The offside lines can be nested, and are pushed and popped as you would expect:
New offside lines can never go forward further than the previous line on the stack:
Special cases ##
There are number of special cases which have been created to make code formatting more flexible. Many of them will seem natural, such as aligning the start of each part of an if-then-else
expression or a try-catch
expression. There are some non-obvious ones, however.
Infix operators such as "+", "|>" and ">>" are allowed to be outside the line by their length plus one space:
If an infix operator starts a line, that line does not have to be strict about the alignment:
If a "fun
" keyword starts an expression, the "fun" does not start a new offside line:
Finding out more
"Verbose" syntax
By default, F# uses indentation to indicate block structure -- this is called "light" syntax. There is an alternative syntax that does not use indentation; it is called "verbose" syntax. With verbose syntax, you are not required to use indentation, and whitespace is not significant, but the downside is that you are required to use many more keywords, including things like:
"
in
" keywords after every "let" and "do" binding"
begin
"/"end
" keywords for code blocks such as if-then-else"
done
" keywords at the end of loopskeywords at the beginning and end of type definitions
Here is an example of verbose syntax with wacky indentation that would not otherwise be acceptable:
Verbose syntax is always available, even in "light" mode, and is occasionally useful. For example, when you want to embed "let" into a one line expression:
Other cases when you might want to use verbose syntax are:
when outputting generated code
to be compatible with OCaml
if you are visually impaired or blind and use a screen reader
or just to gain some insight into the abstract syntax tree used by the F# parser
Other than these cases, verbose syntax is rarely used in practice.
Last updated
Was this helpful?