Skip to content

eventhorizn/f-sharp-bootcamp

Repository files navigation

F# Bootcamp

Getting Set Up

  1. .Net Core Installed
  2. Ionide extension
  3. Bracket Pair Colorizer 2

Managing a Project

  1. Create a project
    dotnet new console -lang F#
  2. Running a project
    dotnet run
  3. Debugging
    • Use the Ionide extension
    • Can set up VS code debugging files, but annoying

F# Notes

  1. Last line in a function is what is returned
  2. Array: array.[0] (annoying .)
  3. Format specifiers are strongly typed
    printfn "Hello %s from my F# program" person
  4. Minimize mutable variables
    let mutable person = "Anonymous Person"
  5. When to use type annotations?
    • Type inference is pretty good for the most part
    • Type methods usually require type annotations
    let fromStringOr (d: float) (s: string) : float =
         s
         |> tryFromString
         |> Option.defaultValue d
    • Example, not really needed
  6. When adding files, use ionide ext
    • My guess, is you need to do reference stuff too
    • File order matters in compilation
  7. Records vs Anonymous Records
    • {...} means record type
    • {|...|} means anonymous record
    • You'd use anonymous records when returning tupules
      • Gives you named fields to use
  8. Arrays are mutable

Forward Piping

  1. Arguments often have no brackets
    • Separated by spaces
    let arrayIter argv =
      Array.iter sayHello argv
      printfn "Nice to meet you"
    • This serves a purpose: forward piping
  2. Forward pipe operator
    argv
     |> Array.filter isValid
     |> Array.iter sayHello

Modules

  1. Modules are used to associate record types w/ functionality
  2. Use the same name b/t a record and a module
type Student =
    {
        Name: string
        Id: string
        MeanScore: float
        MinScore: float
        MaxScore: float
    }

module Student =
    let fromString (s: string) =
        let elements = s.Split('\t')
        let name = elements.[0]
        let id = elements.[1]
        let scores =
            elements
            |> Array.skip 2
            |> Array.map float
        let meanScore = scores |> Array.average
        let minScore = scores |> Array.min
        let maxScore = scores |> Array.max
        {
            Name = name
            Id = id
            MeanScore = meanScore
            MinScore = minScore
            MaxScore = maxScore
        }

    let printSummary (student: Student) =
        printfn "%s\t%s\t%0.1f\t%0.1f\t%0.1f" student.Name student.Id student.MeanScore student.MinScore student.MaxScore

Parameters

  1. Curried parameters

    let fromStringOr d s: float =
    s
    |> tryFromString
    |> Option.defaultValue d
    • Function parameters which support partial application
    • When an argument is supplied, the result is a function that expects any remaining params
    • When all args have been supplied, the function returns a result
    let add a b =
     a + b
    
    let c = add 2 3
    
    // not an error
    // partial application of curried function
    let d = add 2
    
    let e = d 4
    • This causes lots of bugs due to it not throwing an error, but just returning a function
  2. Tupule Parameters

    let fromStringOr (d, s): float =
       s
       |> tryFromString
       |> Option.defaultValue d

Discriminated Unions & Match Expressions

  1. Discriminated Unions
type TestResult =
    | Absent
    | Excused
    | Voided
    | Scored of float
  1. Match Expressions
 let tryEffectiveScore (testResult: TestResult) =
        match testResult with
        | Absent -> Some 0.0
        | Excused
        | Voided -> None
        | Scored score -> Some score
  • The default case

    let nameParts (s: string) =
          let elements = s.Split(',')
          match elements with
          | [|surname; givenName|] ->
             surname.Trim(), givenName.Trim()
          | _ ->
             raise (System.FormatException(sprintf "Invalid name format: \"%s\"" s))
  • Use rarely as when you add a new item in the union, you may forget to add a handle in the match expression

    • Which would then hit the default case

Array Comprehension

let todayIsThursday() =
   DateTime.Now.DayOfWeek = DayOfWeek.Thursday

let isEven x =
   x % 2 = 0

// The code can be complex
let evenNums =
   [|
      if todayIsThursday() then 42
      for i in 1..9 do
            let x = i * i
            if x |> isEven then
               x // implicit yield
      999
   |]

Sequences

  1. Similar to array, but it doesn't store everything like an Array
  2. Type IEnumerable
  3. Generates values on demand
let totalSeq =
   seq { for i in 1..1000 -> i * i }
   |> Seq.sum
  1. If you consume them multiple times, you'd be calculating them multiple times
    let totalSeq =
       seq { for i in 1..1000 -> i * i }
       |> Seq.sum
       |> seq.cache
    • Seq.toArray
  2. When to use a sequence?
    • Collection only exists to be summarized or piped
    • Long in theory but only some elements accessed
  3. When to use an array?
    • Iterated over/consumed multiple times
    • Elements access by index
    • You need to test your program w/ realistic amounts of data

Lists

  1. Similar to arrays, but immutable
  2. It's also a linked list, not a contiguous block of memory

Next Steps

  1. Nuget
  2. Option.map, Option.bind
  3. SAFE Stack and Fable for Web UI development

About

F# from the ground up

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages