Skip to content

Instantly share code, notes, and snippets.

@M-Mueller
Last active December 26, 2021 19:18
Show Gist options
  • Save M-Mueller/3390ca89eb7233b80ac2731a7fc704c8 to your computer and use it in GitHub Desktop.
Save M-Mueller/3390ca89eb7233b80ac2731a7fc704c8 to your computer and use it in GitHub Desktop.
F# Cheatsheet

Syntax

Discriminated Unions / Union Types / ADTs

type Color =
  | Red
  | Green
  | Blue
  
type BinaryTree =
  | Node of BinaryTree * BinaryTree
  | Leaf

Records

type Point = { X: float; Y: float; Z: float; }

type User =
    { firstname: string
      lastname: string
      age: int }
      
# Type is infered automatically...
let p1 = { X = 1.0; Y = 2.0; Z = -1.0 }
# ...or explicitly
let p2 = { Point.X = 1.0; Y = 2.0; Z = -1.0 }

# Update syntax
let p3 = { p1 with Z = 5.0 }

# Anonymous record
let foo = {| bar = 5; baz = "test" |}

Discriminated Unions with Records

type BinaryTree =
  | Node of Node
  | Leaf
and Node =
  { name: string
    left: BinaryTree
    right: BinaryTree }

Doc for the and keyword

Tuples

let t:string*int*float = ("one", 1, 2.0)
let (a, b, c) = ("one", 1, 2.0)

Strings

let world = "World"
let multiline = """
  Hello
  World!"""
let interpolated = $"Hello {world}!"

Interpolated Strings

Check if string is empty:

System.String.IsNullOrEmpty "test"

Match Expression

match color with
| Red -> "red"
| Green -> "green"
| Blue -> "blue"

Function vs. Methods

There doesn't seem a generic way to call an instance method like a regular function but most builtin types provide a lowercase function for each uppercase method:

let list = [1..10]
let len1 = List.length list
let len2 = list.Length

let vs. use

Both are basically the same, but use calls Dispose when the variable goes out of scope. Details

Type Annotations

Generics can be postfix ML style (int list) or prefix .NET style (list<int>). Postfix should only be used for list, option, voption, array and ref.

Libraries

Testing

xUnit

Works well enough and can be used with different assertion libraries. A test looks like this:

open Xunit

[<Fact>]
let ``test add`` () =
    Assert.Equal(5, 3 + 2)

FsUnit

Alternative assertions for xUnit and others.

open Xunit
open FsUnit.Xunit

[<Fact>]
let ``test add`` () =
    3 + 2 |> should equal 5

Unfortunately, the assertion are basically untyped which leads to issues with e.g. Result. Also the error messages for failed assertions are confusing.

Alternative assertions for xUnit and others based on Quoted Expressions.

open Xunit
open Swensen.Unquote

[<Fact>]
let ``test add`` () =
    test <@ 2 + 3 = 5 @>

Gives very nice error messages for failed assertions.

Property-based testing. Works with different testing frameworks including xUnit and Unquoted.

open Xunit
open FsCheck.Xunit
open Swensen.Unquote

[<Property>]
let ``test reverse`` (l:list<int>) =
    test <@ (List.reverse (List.reverse l)) == l @>

GUI

Avalonia + FuncUI

Works nice for standard elements but XAML didn't convince me.

SQL

SQLProvider

Converts LINQ to SQL. Couldn't get this to work with SQLite and gave up after one hour.

Dapper.FSharp

Maps SQL to F# records. SQL queries are written with Computation Expressions and are therefore typesafe to some extend. However, Dapper does not verify queries against the db schema during compile time, e.g. typos in table names will lead to runtime exceptions. The F# records have to reflect the table layout which leads to annoying conversions when the domain model e.g. doesn't include the primary key.

Executes raw SQL queries and provides some helpers to convert the result to F#. Only works with PostgreSQL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment