This video is only a short sample, but you can access the full version and all our other great content by subscribing.

Video

Notes

Compilers are powerful helpers but they can only do so much with primitives. Joël and Stephanie fix a bug by introducing domain-specific types. Learn about how these encode real-world context, what are the downsides, and how some functional programming concepts can make those downsides go away.

Primitive Obsession

Previously discussed, don't depend too heavily on low-level constructs.

Dangerous

Two Ints can bean different things and easily be confused for each other

type alias User =
  { age : Int
  , salary : Int
  , balance : Int
  }

payday : User -> User
payday user =
  { user | balance = pay user.age user.salary }

pay : Int -> Int -> Int
pay salary age =
  salary + (age * 10)

Compiler to the rescue

type Dollar = Dollar Int

type alias User =
  { age : Int
  , salary : Dollar
  , balance : Dollar
  }

payday : User -> User
payday user =
  { user | balance = pay user.age user.salary }

pay : Dollar -> Int -> Int
pay (Dollar salary) age =
  salary + (age * 10)

gives error

The 1st argument to function pay is causing a mismatch.
Function pay is expecting the 1st argument to be:

Dollar

But it is:

Int

Adding dollars

Wrapping, doing thing, and re-wrapping usually is the sign of a map function

module Dollar exposing(Dollar, fromInt, map2)

type Dollar = Dollar Int

fromInt : Int -> Dollar
fromInt =
  Dollar

map2 : (Int -> Int -> Int) -> Dollar -> Dollar -> Dollar
map2 f (Dollar d1) (Dollar d2) =
  Dollar <| f d1 d2
payday : User -> User
payday user =
  { user | balance = Dollar.map2 (+) user.balance (pay user.age user.salary) }

Getting fancy

With map and map2, we can implement most custom operations

plus : Dollar -> Dollar -> Dollar
plus =
  map2 (+)

minus : Dollar -> Dollar -> Dollar
minus =
  map2 (-)

times : Int -> Dollar -> Dollar
times n =
  map ((*) n)

divideBy : Int -> Dollar -> Dollar
divideBy n =
  map (\d -> d / n)

square : Dollar -> Dollar
square =
  map (\d -> d ^ 2)

Some articles: * Avoiding primitives in Elm * Algebraic blindness

Ideas described here implemented as a package.