2

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 }
glennsl
  • 28,186
  • 12
  • 57
  • 75
Kirk Strobeck
  • 17,984
  • 20
  • 75
  • 114

1 Answers1

2

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.

glennsl
  • 28,186
  • 12
  • 57
  • 75
  • That’s great! Thx. Unfortunately, I’m still running up against an error though `Port reactToElm is trying to communicate an unsupported type. The specific unsupported type is the following extended record: (example shown). The types of values that can flow through in and out of Elm include: Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples, Json.Values, and concrete records.` – Kirk Strobeck Sep 30 '18 at 17:59
  • 1
    Hmm, no idea about that one. I've not even used ports yet, but that's an interesting issue that I'd love to see an answer to. You ought to post it as a separate question with a complete example I think. – glennsl Sep 30 '18 at 21:18
  • 1
    @KirkStrobeck The message tells you exactly what types can be automatically converted to JSON. Not included, for example, is your `Nav` type. The standard solution is to use the JSON package to have a way to encode your value into a `Json.Value` which, as the documentation suggests, can be passed through a port. – Mark Bolusmjak Oct 01 '18 at 14:57
  • @z5h While you're right that this wouldn't work anyway because of the custom type, it complains specifically about the use of "extended record" and I'm very curious about the difference between a concrete record and an extended record that has been fully "applied". I can't see that there should be any difference at runtime. – glennsl Oct 02 '18 at 11:10