Program layout

For output, use printf and printfn.

If you don't need to read input, you can just write top-level statements:

for i=1 to 10 do printfn"Hello, world! %i"i

Otherwise, you have to (quite vexingly) annotate a Array<string> -> int function definition with [<EntryPoint>].

// This is almost the shortest…
[<EntryPoint>]let m a=
for x in a do printfn"%s"x
0


// This saves a byte! But you can't use "let" as easily in a one-liner like this.
[<EntryPoint>]let m a=[for x in a->printfn"%s"x];0

Printing a bunch of hardcoded small numbers

Try some variation on this:

Seq.iter(printfn"%i")"data_here_123"B

"..."B is a byte[] literal. You can actually put Unicode up to U+00FF in there.

Conditional output

if x<5 then printfn"%i"x
x<5&&()=printfn"%i"x  // compare the `unit` result of printfn to `() : unit` to make a `bool`!
x<5&()=printfn"%i"x   // honestly no idea why this works (causes a warning)

Defining operators

As in Haskell, this is often shorter than giving helper functions alphabetic names.

You can overwrite any operator and steal its precedence! Pick one that saves parens. You can even redefine unary plus (~+) etc.

let(%)=max
printfn"%A"((3+4)%5)

let(-)=max
printfn"%A"(3+4-5)

Mutation

Either use ref or let mutable:

Operation ref let mutable
Creation let x=ref 1 let mutable x=1
Multiple creation let x,y=ref 1,ref 2 let mutable x,y=1,2
Type int ref int
Access !x x
Modification x:=2 x<-2

Arrays are always mutable, so let x=[|1|] followed by x.[0]<-2 also works.

Of course, also consider the more "FP golf" tricks making your state variable the accumulator of a Seq.fold or an argument of let rec(%), which is often shorter.

let mutable a=1␤for i in 1..5 do printfn"hey %i"a;a<-a*2
let a=ref 1␤for i in 1..5 do printfn"hey %i"!a;a:=!a*2
let rec(=)a i=printfn"hey %i"a;i<5&a*2=i+1␤1=1
Seq.fold(fun a _->printfn"hey %i"a;a*2)1[1..5]