F#
Quickstart
- functional
- supports object-oriented patterns
- foss
- runs on linux, osx, windows
Comments
// single line comment
(* multi
line
comment,
similar
to
OCaml *)
Printing
// ---------- PRINT ----------
// printf and printfn can be used to directly print string literals
printf "hello world\n" // printf prints without the newline
printfn "hello world" // printfn prints with a newline character
// ---------- FORMATTED STRING ----------
// $ and {} used for string interpolation
// both printf and printfn can print formatted strings with format specifiers using the syntax of {PRINT FUNCTION} {STRING WITH FORMAT SPECIFIFERS} {VARIABLES}
// %s => string
// %d => decimal
// %i => integer
// %x => hexadecimal
// %f => floating point number
// %e => exponential
// %g => general format (automatically chooses between %f and %e)
// %b => boolean
// %c => character
// %A => prints values using F#'s default formatting
// %Ns => specifies a minimum field width of N characters for strings
// %Nd => specifies a minimum field width of N characters for decimals
// %Ni => specifies a minimum field width of N characters for integers
// %N.Mf => specifies a minimum field width of N characters and M decimal places for floating point numbers
let dish = "cereal chicken"
let cost = 2.50
let finSentence = $"This {dish} costs {cost}"
printfn "%s" finSentence // printing of a string variable using string interpolation
Variables
// ---------- VARIABLE ----------
// let defines an immutable variables
// type declaration is not required
let anInt = 5
let myFloat = 3.14
let myString = "smacks"
// NOTE
= // both the assignment operator AND equality check operator (for equality checks similar to == or === in other languages)
Types
// ---------- TYPE ----------
// expressions and values are immutable by default
// int => 32-bit signed int (42)
// int64 => 64-bit signed int (10000)
// float => 64-bit floating point number (3.42)
// char => single unicode character, single-quoted ('a')
// string => sequence of characters, double-quoted ("watermelon")
// bool => boolean value (true, false)
Data structures
// ---------- LIST ----------
// lists are immutable, so any functions or operators that are called on the list return a copy or must be reassigned
// ordered collections of elements of the same type
// lists are wrapped with [] square brackets, and ; semicolon delimited (instead of the usual commas)
let aList = [2; 3; 4; 5]
// LIST METHODS
// see below for other list functions
let addedToList = 1 :: aList // :: adds an element to the front of the list, so this evaluates to [1; 2; 3; 4; 5]
let concatTwoLists = [0; 1] @ addedToList // @ concatenates two lists together, so this evaluates to [0; 1; 1; 2; 3; 4; 5]
let aListOfRange = [1..10] // .. defines an inclusive range within the list, so this evaluates to [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
let aListFromSeqExp = [for i in 1..10 -> i * i] // lists can be defined with sequence expressions as well, as seen here this creates a list of squares of integers 1 to 10, which evaluates to [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
let aListFromListComprehension = [for i in 1..10 do yield i * i] // basically the same as above but achieved via list comprehension
// --------- ARRAY ----------
// arrays are mutable and considered more efficient
// ordered collection of elements of the same type
// arrays are wrapped with [||] square brackets with bars and ; semicolon delimited
let anArray = [|"a"; "b"|]
// ---------- SEQUENCE ----------
// infinite sequence of elements
// basically an enumerator
// sequences are wrapped with {} curly braces and ; semicolon delimited and accompanied with seq
// yield is used to generate sequence values where required
let aSequence = seq {yield "a"; yield "b"}
// ---------- TUPLE ----------
// collecton of elements of any data type
// tuples are anonymous by default
// , comma delimited
// can be unpacked with pattern matching similar to Rust
let aTuple = 1,2
let anotherTuple "a", 2, true
let x,y = aTuple // this unpacks the tuple and sets the value of x = 1, y = 2
Functions
// ---------- FUNCTION ----------
// let defines a function, which is defined similar to a variable (since we're in functional land where every statement is an expression that evaluates to a value including functions)
// no brackets and implicit return of last expression similar to Haskell
// functions are first class entitites and can be chained to create powerful constructs
// let {FUNCTION NAME} {FUNCTION PARAMETERS} = {FUNCTION PROCEDURES which are implicitly returned}
// multiline functions can be defined with indents
// () brackets can be used to define function precedence
// |> pipe operator also available to pipe the output of one operation to another, this is very common in F#
// anonymous functions (lambdas) can be defined with the fun keyword
// modules group functions together (indentation necessary for each nested module)
let square x = x * x // note that parameter and return values are effectively non-distinguishable
square 3 // evaluates to the value of 9
let add x y = x + y // implicit return of the x + y calculated value
add 2 3 // runs the function, evaluates to the value of 5
// MULTILINE FUNCTION
// mainly used for greater readibility
let evens list =
let isEven x = x % 2 = 0 // defining the sub function isEven within the multiline function evens so that it can only be referenced within the evens function, also the first = is an assignment of an expression, the second = is an equality check
List.filter isEven list // built-in function List.filter then called on each value of the list parameter received by evens function based on the conditional check laid out in the defined sub function isEven
evens [1; 2; 3; 4; 5] // this runs the above function as expected
// FUNCTION PRECEDENCE
// () define function precedence, and help readibility
let sumOfSquaresTo100 = List.sum ( List.map square [1..100] ) // here, the brackets specify the contents of List.map is to be called on the in t list defined for range 1-100, mapping the square function on each value, the returned value then being passed to List.sum
// PIPING
// |> lets you pipe
let sumOfSquaresTo100WithPipes = [1..100] |> List.map square |> List.sum // this does the same thing as above
// ANONYMOUS FUNCTIONS
// fun defines a lambda function for a one-time use function
let sumOfSquaresTo100ButLambda = [1..100] |> List.map (fun x -> x * x) |> List.sum // this does the same as the above code except it defines its own anonymous function using the fun keyword
// MODULES
// module and indentation groups functions together
// note there is no let when defining a module, just module
module SimpleMathThingies =
let add x y = x + y
let subtract x y = x - y
let multiply x y = x * y
let divide x y = x / y
let modulo x y = x % y
let square x = x * x
Control structures
// ---------- PATTERN MATCHING ----------
// match with | -> allows for supercharged case switch statement, just like Rust
// _ is the catch-all operator also similar to Rust
// everything is still an expression, so everything evaluates to a value and is defined with let, even pattern matches
// nested definition of expression!
// pattern matching works for lists and other data structures as well
let simplePatternMatch =
let x = "a"
match x with
| "a" -> printfn "x is a"
| "b" -> printfn "x is b"
| _ -> printfn "catch all operator hit" // F# does not allow nulls by default, an Option type must be used for pattern matching, although None is a valid value
// ---------- IF ELIF ELSE THEN ----------
// F# also has the standard conditional checks, which work as expected
// you can assign boolean expressions to variables and functions
let test x y =
if x = y then "they are equals"
elif x < y then "x is less than y"
else "x is greater than y"
Loops
// ---------- LOOP ----------
// for in do allows you to iterate over a collection of elements
// indentation matters
// loops can be used to iterate over different patterns as well
let list1 = [1; 5; 100; 450; 788]
for i in list1 do
printfn "%d" i
let seq1 = seq {for i in 1..10 -> (i, i*i)}
for (a, asqr) in seq1 do
printfn "%d squared is %d" a asqr
Helpful functions
// ----------- USEFUL ----------
// note that these work for arrays as well, simply replace the List with Array
// so Array.map, Array.filter and Array.iter are all valid
yield // yield allows for lazy evaluation of variables within loop expressions, often used in F# sequences to generate values only when they are required and called
yield! // yield! adds a whole subsequence to a sequence, allowing for concise expressions like yield! [5..10]
List.map // applies a function to each element in the list and returns a copy of the new list
List.iter // applies a function to each element in the list, and used for its side effects, like printing out each element of the list
List.filter // filters elements of a list based on a specified predicate (conditional check)
List.fold // applies a binary function to elements of the list from an initial defined accumalator value
List.reduce // similar to .fold but without an explicit defined accumulator
List.length // returns length of list
List.head // returns first list element of index 0
List.tail // returns last list element of index List.length-1
List.append // concatenates two lists together
List.concat // concatenates a list of lists into a single list, basically flattening it
List.rev // reverses order of elements in the list
List.sort // sorts the elements of the list by value
List.max // returns the element of max value in the list
List.min // returns the element of min value in the list
// read F# documentation for many others
More on
- ref
- map
- set
- type
- union types
- active patterns (if, elif, else, then)
- rec
- async
- .NET compatibility
- OOP extensibility
- try fsharp