🌳
Walking though the Elm woods
  • Introduction
  • Structure of the book
  • Frequently asked questions
    • How can different types share data?
    • How to break Dependency Cycles?
    • How to structure an Elm project?
    • How to turn a Msg into a Cmd Msg?
    • How to update nested Records?
    • What are comparable types?
    • Why are Booleans bad?
    • 🔜Future topics
  • Recipes
    • Writing a Single Page Application
      • Share state across pages
      • Debounced Validation
      • Reusable views
    • Making impossible states Impossible
      • Non empty lists using Zippers
      • Restrict records using Opaque Types
      • Write safer functions using Phantom Types
    • Designing Elm package APIs
      • Create upwards compatible APIs
    • 🔜Future topics
  • Frameworks and packages
    • elm/parser
    • mdgriffith/elm-ui
    • 🔜Future topics
Powered by GitBook
On this page
  • Question
  • Answer
  • Further reading

Was this helpful?

  1. Recipes
  2. Making impossible states Impossible

Write safer functions using Phantom Types

type alias LoginForm =
    { username : String
    , password : String
    , isValid : Bool
    }

{-| Creates a User

Only use this function if the LoginForm was validated.
-}
createUser : LoginForm -> User
createUser loginForm =
    if loginForm.isValid then
        User.create loginForm.username
    else
        Debug.todo "This should not happen."
type LoginForm valid =
    LoginForm
        { username : String
        , password : String
        }

type Valid = Valid

{-| Creates a User
-}
createUser : LoginForm Valid -> User
createUser (LoginForm loginForm) =
        User.create loginForm.username
        
validate : LoginForm () -> LoginForm Valid
validate =
    ...

Question

How can I ensure that a user can only be created with a valid login form?

Answer

Use a so called Phantom Type:

type LoginForm valid = --valid is not used in the definition
    LoginForm
        { username : String
        , password : String
        }
        
type Valid = Valid

Use LoginForm () for unvalidated forms and LoginForm Valid for validated ones.

Further reading

PreviousRestrict records using Opaque TypesNextDesigning Elm package APIs

Last updated 5 years ago

Was this helpful?

📄Article: by Charlie Koster

Advanced Types in Elm - Phantom Types