Search code examples
recordelmrow-polymorphism

Define `type alias` with a partial definition or default


Is there a way to DRY this?

I don’t want to repeat contents : List Nav and wrapperId : String

type alias InputModel =
    { contents : List Nav
    , containerClassName : Maybe String
    , wrapperId : String
    }


type alias Model =
    { contents : List Nav
    , containerClassName : String
    , wrapperId : String
    }


modelWithDefaults : InputModel -> Model
modelWithDefaults input =
    { input | containerClassName = withDefault "" input.containerClassName }

Solution

  • Yes there is! You can move the common fields into a separate record and add a row variable to it. The row variable, a, which specify the remaining fields, can then be supplied later:

    type alias CommonModel a =
        { a
            | contents : List Nav
            , wrapperId : String
        }
    
    
    type alias InputModel =
        CommonModel
            { containerClassName : Maybe String }
    
    
    type alias Model =
        CommonModel
            { containerClassName : String }
    

    You can also use row variables to write functions that accept any record as long as it has the common fields. E.g.

    getWrappedId : CommonModel a -> String
    getWrapperId { wrapperId } = wrappedId
    

    will accept both InputModel and Model, or any other record that contains at least the fields specified by CommonModel. The row variable will be inferred, just like any other type variable would.