Cardinality

(hopefully) explained

What is cardinality?


type Bool = True | False

type Foo = One Bool | Two Bool | Three Bool 

type Bar = (Bool, Bool, Bool)

type Baz = Baz 
   { a : Bool 
   , b : Bool 
   , c : Bool 
   }

Combinations


type Thingy = Foo | Bar | Baz

type Whatsit = (Foo, Bar, Baz)

type Widget = Foo | (Bar, Baz)

Why do we care?


head : List a -> a

head : List a -> Maybe a

head : NonEmptyList a -> a

This is everywhere


divide : Float -> Float -> Float        -- ☠️

type Person = Person 
    { name : String -- ☠️
    , age : Int -- ☠️
    , gender : String -- ☠️
    , postcode : String -- ☠️☠️☠️
    }

type Data = Data 
    { loading : Bool
    , error : Bool
    , stuff : Maybe Stuff
    }

Alternatives?


divide : Float -> NonZeroFloat -> Float        -- ☠️
type Gender = Gender String 
makeGender : String -> Maybe Gender

type Person = Person 
    { name : String -- ☠️
    , gender : Gender
    }
type Data e a
    = Loading 
    | Error e
    | Success a

How do we fix this?

Create the right types

Instantiate them at the right time

Parse. Don't validate.

Show me a swagger schema and I will show you a type with the wrong cardinality

Things to watch out for

  • Ad hoc validation (aka shotgun parsing)
  • if without else
  • Automatic deserialisation
  • Mutable types / setters
  • Public / multiple constructors
  • Void validation functions
  • Product types with nullable properties (they are sum types in disguise)
  • Nullable types

Valid values

Possible values

Input

Good modelling

* the red is "stuff you shouldn't have to deal with"

Valid values

Questions

Cardinality Explained

By Julian Jelfs

Cardinality Explained

Discussion about what cardinality is and how it is related to data modelling and code quality.

  • 110