🌳
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

Restrict records using Opaque Types

PreviousNon empty lists using ZippersNextWrite safer functions using Phantom Types

Last updated 4 years ago

Was this helpful?

This topic has been covered by

If you're writing a package use a instead.

Main.elm
type alias Movie = 
    { title : String
    , rating : Int
    }

{-| adds a Rating to a Movie,
Only allows ratings between 1 and 5.
-}     
addRating : Int -> Movie -> Movie
addRating rating movie =
    { movie | rating = rating}
Movie.elm
module Movie exposing (Movie,fromTitle,addRating)

type Movie =
    Movie 
        { title : String
        , rating : Int
        }

{-| adds a Rating to a Movie,
Only allows ratings between 1 and 5.
-}     
addRating : Int -> Movie -> Maybe Movie
addRating rating (Movie movie) =
    if 1 <= rating && rating <= 5 then 
        Just <| Movie { movie | rating = rating}
    else
        Nothing

{-| Constructs a Movie from a Title
-}
fromTitle : String -> Movie
fromTitle title =
  Movie
    { title = title
    , rating = 0
    }
Main.elm
import Movie exposing (Movie)

newMovie : String -> Int -> Movie
newMovie title rating =
    let
        movie = Movie.fromTitle title
    in
    movie
    |> Movie.addRating rating
    |> Maybe.withDefault movie

Question

How can I only allow specific states for my type?

Answer

Place the type into a new File and don't expose the constructor: exposing (Movie) instead of exposing(Movie(..)). Then write your own constructors.

We can unwrap function parameters like this:

addRating rating (Movie movie) =
    ...

Further reading

📖Book: by Pawan Poudel

📄Article: by Charlie Koster

👥Thread:

Elm Patterns
pipeline API
Beginning Elm
Advanced Types in Elm - Opaque Types
Post-update invariant repair: good idea or bad?