Should decoder and record be fields order independant?


UserRecordDecoder =
  D.map2 UserRecord
    (D.field "id"
    (D.field "level"
type alias UserRecord =
  { id : Int
  , level : Int

will do the job however if we inverse the order of the fields like this

type alias UserRecord =
  { level : Int
  , id : Int

for a user { id = 1, level = 10 } you will get { id = 10, level = 1 }

Would it be better and possible not to assert that the orders of decoder and record match when decoding json and decode just according to the field names?


1 Like

This package helps with decoding json in a way that the order does not matter. It also has some more info on the subject.


It looks pretty good, tks!

I started to use it, documentation is great, thanks to have spent time on it!
With elm-format the nested scopes show up !
And I got a better understanding of the do notation (Decode.succeed == return :sunny: ), I love functional ~

UserRecord_decoder : D.Decoder UserRecord
UserRecord_decoder =
    Field.require "name" D.string
        <| \name ->
            Field.require "email" D.string
                <| \email ->
                    Field.require "phone" D.string
                        <| \phone ->
                                { email = email
                                , phone = phone
                                , name = name

I am glad you found it helpful. The indenting done by elm-format can be annoying sometimes, at least when you have many fields.

The do notation-ish syntax can be used with any type that has the andThen function and can be quite useful sometimes.

Here is an example with Maybe. (Note that Just == return in this case)

do : Maybe a -> (a -> Maybe b) -> Maybe b
do mb continuation =
    Maybe.andThen continuation mb

getSomething : String -> Dict String String -> Maybe String
getSomething key dict =
    do (Dict.get key dict) <| \value1 ->
    do (Dict.get value1 dict) <| \value2 ->
    do (Dict.get value2 dict) <| \value3 ->
    Just value3
1 Like
do : Maybe a -> (a -> Maybe b) -> Maybe b
do mb continuation =
    Maybe.andThen continuation mb

getSomething acc key dict = -- acc: accumulator
    do (Dict.get key dict) <| \value1 ->
    do (Dict.get value1 dict) <| \value3 ->
    (dict, Just value3 :: acc)

getSomethingElse acc key dict =
    do (Dict.get key dict) <| \ values ->
    do (Dict.get value1 dict) <| \value2 ->
    (dict, Just value2 :: acc])

do (getSomething [] "Lisa" dict) <| \(dict, acc) -> getSomethingElse acc "Tom" dict -- return (dict,  [ Just value2, Just value3])

In another way how can I make it composable?
Is the code above making sense?
Is there a better way?

You can also not use the alias constructor, which is what relies on order, like this:

userRecordDecoder =
    D.map2 (\id level -> { id = id, level = level })
        (D.field "id"
        (D.field "level"

And then your decoder is independent of the type reordering it’s fields.


It is worth mentioning that!

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.