Skip to content

Instantly share code, notes, and snippets.

@JoelQ
Last active October 5, 2017 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoelQ/5b03a40cd31c6c0fcd03c51bdc4b096b to your computer and use it in GitHub Desktop.
Save JoelQ/5b03a40cd31c6c0fcd03c51bdc4b096b to your computer and use it in GitHub Desktop.
Wrapping primitives in custom types in Elm

Domain-Specific Types in Elm

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:

Ideas described here implemented as a package.

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