type ContactType =
  | Twitter
  | Blog
  | GitHub

Full name: index.ContactType
union case ContactType.Twitter: ContactType
union case ContactType.Blog: ContactType
union case ContactType.GitHub: ContactType
val getContactInfo : _arg1:ContactType -> string

Full name: index.getContactInfo
val name : string

Full name: index.name
val sayHello : n:string -> unit

Full name: index.sayHello
val n : string
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val add1mult2 : (int -> int)

Full name: index.add1mult2
val xs : int list

Full name: index.xs
val req : System.Net.WebRequest

Full name: index.req
namespace System
namespace System.Net
type WebRequest =
  inherit MarshalByRefObject
  member Abort : unit -> unit
  member AuthenticationLevel : AuthenticationLevel with get, set
  member BeginGetRequestStream : callback:AsyncCallback * state:obj -> IAsyncResult
  member BeginGetResponse : callback:AsyncCallback * state:obj -> IAsyncResult
  member CachePolicy : RequestCachePolicy with get, set
  member ConnectionGroupName : string with get, set
  member ContentLength : int64 with get, set
  member ContentType : string with get, set
  member CreatorInstance : IWebRequestCreate
  member Credentials : ICredentials with get, set
  ...

Full name: System.Net.WebRequest
System.Net.WebRequest.Create(requestUri: System.Uri) : System.Net.WebRequest
System.Net.WebRequest.Create(requestUriString: string) : System.Net.WebRequest
property System.Net.WebRequest.Timeout: int
val success : bool * int

Full name: index.success
val isSuccess : bool

Full name: index.isSuccess
val value : int

Full name: index.value
type Order =
  {OrderId: int;
   CustomerId: int;
   OrderItems: string list;}

Full name: index.Order
Order.OrderId: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Order.CustomerId: int
Order.OrderItems: string list
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
type ValidationResult =
  | OK
  | Fail of (string * string) list
  | Error of Exception

Full name: index.ValidationResult
union case ValidationResult.OK: ValidationResult
union case ValidationResult.Fail: (string * string) list -> ValidationResult
union case ValidationResult.Error: System.Exception -> ValidationResult
Multiple items
type Exception =
  new : unit -> Exception + 2 overloads
  member Data : IDictionary
  member GetBaseException : unit -> Exception
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member GetType : unit -> Type
  member HResult : int with get, set
  member HelpLink : string with get, set
  member InnerException : Exception
  member Message : string
  member Source : string with get, set
  ...

Full name: System.Exception

--------------------
System.Exception() : unit
System.Exception(message: string) : unit
System.Exception(message: string, innerException: exn) : unit
Multiple items
module Option

from Microsoft.FSharp.Core

--------------------
type Option<'a> =
  | Some of 'a
  | None

Full name: index.Option<_>
union case Option.Some: 'a -> Option<'a>
union case Option.None: Option<'a>
val fib : n:int64 -> int64

Full name: index.fib
val n : int64
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int64

--------------------
type int64 = System.Int64

Full name: Microsoft.FSharp.Core.int64

--------------------
type int64<'Measure> = int64

Full name: Microsoft.FSharp.Core.int64<_>
val loop : (int64 -> int64)
val x : int64
Multiple items
type Decimal =
  struct
    new : value:int -> decimal + 7 overloads
    member CompareTo : value:obj -> int + 1 overload
    member Equals : value:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val Zero : decimal
    static val One : decimal
    static val MinusOne : decimal
    static val MaxValue : decimal
    ...
  end

Full name: System.Decimal

--------------------
System.Decimal()
System.Decimal(value: int) : unit
System.Decimal(value: uint32) : unit
System.Decimal(value: int64) : unit
System.Decimal(value: uint64) : unit
System.Decimal(value: float32) : unit
System.Decimal(value: float) : unit
System.Decimal(bits: int []) : unit
System.Decimal(lo: int, mid: int, hi: int, isNegative: bool, scale: byte) : unit
System.Decimal.TryParse(s: string, result: byref<decimal>) : bool
System.Decimal.TryParse(s: string, style: System.Globalization.NumberStyles, provider: System.IFormatProvider, result: byref<decimal>) : bool
val v : decimal
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val printFirstItems : order:Order -> unit

Full name: index.printFirstItems
val order : Order
val x : string
val y : string
val printResult : result:ValidationResult -> unit

Full name: index.printResult
val result : ValidationResult
val es : (string * string) list
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IReadOnlyCollection<'T>
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
val p : string
val m : string
val e : System.Exception
namespace System.Text
namespace System.Text.RegularExpressions
val pattern : string
val v : string
val m : Match
Multiple items
type Regex =
  new : pattern:string -> Regex + 2 overloads
  member GetGroupNames : unit -> string[]
  member GetGroupNumbers : unit -> int[]
  member GroupNameFromNumber : i:int -> string
  member GroupNumberFromName : name:string -> int
  member IsMatch : input:string -> bool + 1 overload
  member Match : input:string -> Match + 2 overloads
  member MatchTimeout : TimeSpan
  member Matches : input:string -> MatchCollection + 1 overload
  member Options : RegexOptions
  ...

Full name: System.Text.RegularExpressions.Regex

--------------------
Regex(pattern: string) : unit
Regex(pattern: string, options: RegexOptions) : unit
Regex(pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : unit
Regex.Match(input: string, pattern: string) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : Match
property Group.Success: bool
val x : Group
property Match.Groups: GroupCollection
property Capture.Value: string
val skip : count:int -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.skip
type EmailAddress = string

Full name: index.EmailAddress
Multiple items
union case VerifiedAddress.VerifiedAddress: EmailAddress -> VerifiedAddress

--------------------
type VerifiedAddress = | VerifiedAddress of EmailAddress

Full name: index.VerifiedAddress
type EmailContact =
  | Unverified of EmailAddress
  | Verified of VerifiedAddress

Full name: index.EmailContact
union case EmailContact.Unverified: EmailAddress -> EmailContact
union case EmailContact.Verified: VerifiedAddress -> EmailContact
val verifyEmailAddress : 'a -> Option<VerifiedAddress>

Full name: index.verifyEmailAddress
val sendPasswordReset : VerifiedAddress -> unit

Full name: index.sendPasswordReset
val email : EmailAddress

Beyond the Basics with F#

F#

About me

mvp
  • Shane Charles
  • White Light Computing, Inc.
  • Functional programming enthusiast
  • Board member for Winnipeg .Net UG
1: 
2: 
3: 
4: 
5: 
6: 
type ContactType = | Twitter | Blog | GitHub

let getContactInfo = function
  | Twitter -> "@dead_stroke"
  | Blog    -> "https://geekeh.com"
  | GitHub  -> "shanecharles"

Path

  • Whirlwind Tour of F#
  • Recursion
  • Active Patterns
  • Domain Models
  • Computation Expressions
  • Azure Function
  • Summary

Why Functional

  • Step outside our known world
  • Immutability
    • Different way of thinking
    • Parallel processing
    • Predictability
  • smaller pieces for a larger solution

F#

  • General purpose language on the CLR
  • Functional First
  • Immutable by default
  • Structural equality by default
  • Type Inference

Keywords

  • let
  • use
  • rec
  • type

Hello, Operator?

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
// (=) equality with value binding
let name = "F#" 

// (=) function definition
let sayHello n = printfn "Hello %s" n 

// (|>) Forward pipe for left to right readability
name |> sayHello  

// (>>) Compose functions
let add1mult2 = ((+) 1) >> ((*) 2)

// (::) Cons operator, append at beginning of list
let xs = 42 :: [ 2; 24 ]

Necessary Evil

1: 
2: 
3: 
// (<-) Mutation!
let req = System.Net.WebRequest.Create "https://google.ca"
req.Timeout <- 30000

Reading F#

Arrows

Tuples

Grouping of seemingly unrelated values.

1: 
2: 
let success = (true, 5)
let (isSuccess, value) = success

Tuples Don'ts

  • No more than 3 values
  • Don't pass them as data

Records

Grouping of read-only named values.

1: 
2: 
3: 
type Order = { OrderId    : int
               CustomerId : int
               OrderItems : string list }

Discrimanted Unions

A choice of one or the other.

1: 
2: 
3: 
4: 
type ValidationResult = 
  | OK
  | Fail of (string * string) list
  | Error of System.Exception

Option

  • Avoid nulls
1: 
2: 
3: 
type Option<'a> = 
    | Some of 'a
    | None

Currying

Functions accepting multiple arguments are converted to multiple functions accepting one argument.

Higher Order Functions

A function that returns a function or takes a function as an argument.

Recursion

A function that calls itself to apply the same rules repeatedly.

Fibonacci

  • It works, performant?
1: 
2: 
3: 
4: 
5: 
6: 
let fib (n : int64) =
  let rec loop x =
    if x <= 1L then x
    else
      loop (x-1L) + loop (x-2L)
  loop n

Recursive Optimizations

  • Tail Call Optimized
  • Memoization

Recursion Alternatives

  • Seq.map
  • Seq.filter
  • Seq.scan
  • Seq.chunkBySize
  • List.partition
  • Seq.fold
  • Seq.unfold

Pattern Matching

Deconstruct all the things.

1: 
2: 
3: 
match System.Decimal.TryParse "3.14159" with
| true, v  -> v
| false, _ -> failwith "Womp womp" 

Matching Lists

1: 
2: 
3: 
4: 
5: 
let printFirstItems (order : Order) = 
  match order.OrderItems with
  | []          -> printfn "No Items"
  | [x]         -> printfn "Only Item: %s" x
  | x :: y :: _ -> printfn "First 2: %s and %s" x y

Matching Discriminated Unions

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let printResult result =
  match result with
  | OK      -> printfn "Everything Worked"
  | Fail [] -> printfn "It's fine"
  | Fail es -> printfn "Errors were reported:" 
                   es |> List.iter 
                        (fun (p,m) -> printfn "- %s: %s" p m)
  | Error e -> printfn "!!System crashed... Reboot!!"
                   printfn "%A" e

Active Patterns

Active patterns allow us to extend the default matching capabilities in F#.

  • Hide some of the ugly code.

4 Types of Active Patterns

  • Single Case Transform
  • Partial Pattern
  • Parameterized Pattern
  • Complete Pattern

Partial Pattern / Parameterized

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
open System.Text.RegularExpressions

let (|RegexMatch|_|) (pattern : string) v =
  let m = Regex.Match (v, pattern)
  if m.Success
  then [ for x in m.Groups do 
             yield x.Value ] 
       |> List.skip 1   
       |> Some
  else None

Active Pattern Warning

  • Don't go to the database
  • No long running processes
  • Maximum 7 choices for Complete Pattern

Domain Modelling

  • Single case discriminated unions
  • Make illegal states unrepresentable

Ensure Correctness

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type EmailAddress = string 

type VerifiedAddress = VerifiedAddress of EmailAddress

type EmailContact =
    | Unverified of EmailAddress
    | Verified of VerifiedAddress

let verifyEmailAddress (emailAddress * isVerified) =
    if isVerified then Some (VerifiedAddress emailAddress)
    else None

let sendPasswordReset (VerifiedAddress email) = 
    printfn "Password reset sent to: %s" email

Computation Expressions

a convenient syntax for writing computations that can be sequenced and combined using control flow constructs and bindings.

Wait?

what

Tough to Explain not Too Difficult to Use

  • Builders or workflows
  • async { }
  • seq { }
  • ! bang notation

Writing Our Own

  • Create a class
  • Minimum required methods
    • bind
    • return

F# Azure Function

  • Azure Function v1:
    • super easy to get started
  • Azure Function v2:
    • Current Status: bring the pain

No More F# Script Support

  • Issues with dotnetcore 2 and FSI
  • Precompiled Only
  • No templates as of September 2018
  • Lot's of steps
  • Tears
  • It will get better

Brief Summary of Pain (MacOS)

  • Install
    • dotnetcore
    • azure-functions-core-tools
    • azure-cli
    • vs code
  • dotnet new F# dll
  • Manual configuration
  • azure command line publish bug supporting only C#
    • 5 stages of grief
  • dotnet publish
  • Deploy via VS Code
  • Profit?

Extra Resources

Thank You

1: 
2: 
3: 
4: 
5: 
6: 
type ContactType = | Twitter | Blog | GitHub

let getContactInfo = function
  | Twitter -> "@dead_stroke"
  | Blog    -> "https://geekeh.com"
  | GitHub  -> "shanecharles"