I actually managed to do a little bit of work on the pole-prediction backend last night. There is a part where we store messages in the “database” (it’s not really a database, it’s really just persistent storage). So the messages are a variant type, and as such we need to write code to both encode each message into JSON, and decode each message from JSON.
type DatabaseMessage = AddDriver Year Driver | AddTeam Year Team | AddEntrant Driver.Id Team encodeDatabaseMessage : DatabaseMessage -> Encode.Value encodeDatabaseMessage dMsg = case dMsg of AddDriver year driver -> [ ( "tag", "AddDriver" |> Encode.string ) , ( "arg1", year |> Encode.int ) , ( "arg2", driver |> Driver.encode ) ] |> Encode.object AddTeam year team -> [ ( "tag", "AddTeam" |> Encode.string ) , ( "arg1", year |> Encode.int ) , ( "arg2", team |> Team.encode ) ] |> Encode.object AddEntrant year driverId team -> [ ( "tag", "AddEntant" |> Encode.string ) , ( "arg1", driverId |> Encode.string ) , ( "arg2", team |> Team.encode ) ] |> Encode.object databaseMessageDecoder : Decoder DatabaseMessage databaseMessageDecoder = let interpret s = case s of "AddDriver" -> Decode.succeed AddDriver |> Decode.andField "arg1" Decode.int |> Decode.andField "arg2" Driver.decoder "AddTeam" -> Decode.succeed AddTeam |> Decode.andField "arg1" Decode.int |> Decode.andField "arg2" Team.decoder "AddEntrant" -> Decode.succeed AddEntrant |> Decode.andField "arg1" Decode.string |> Decode.andField "arg2" Decode.string _ -> Decode.fail (String.append "Unknown message string: " s) in Decode.field "tag" Decode.string |> Decode.andThen interpret As you can see all of this is very repetitive and lends itself well to being automatically generated. You can easily imagine some meta-code that, given a type definition, can automatically generate an encoder and decoder (there is also the elm-codec library but if you’re auto-generating these anyway then that’s less useful).
...