Wednesday, August 26, 2020

Elm JSON Decoder

In the vein of functional programming, I've been learning Elm to visualize the progress of a Sudoku solver.

The JSON decoding tutorials are clear, and the JSON.Decode library is well-documented once you know how to read it (in general, the library documentation in Elm seems to be on the terse side). 

Two things were not very clear from initial read of the docs: how to parse lists to tuples, and how to parse strings to custom types. 

Leaning on SO for the below example,  tuples apparently used to be supported as first-class decoders, but in the current version of Json.Decoder, index seems to be the official way to construct decoders for tuples. 

arrayAsTuple2 a b c =

    Decode.index 0 a

        |> Decode.andThen (\ aVal -> Decode.index 1 b

            |> Decode.andThen (\ bVal -> Decode.succeed (aVal, bVal)))            

boardDecoder : Decoder Board

boardDecoder = Decode.list <| arrayAsTuple2 Decode.int Decode.int 

Elm 0.19 only supports up to three-value tuples (somewhat surprising coming from Haskell, though with records and custom data types, the decision to limit unnamed tuples has a certain logic...). So the helper construction isn't too bad.


Parsing strings to custom data types turns out to be more straightforward, though a bit repetitive (especially if you need to later get the string representation back out of custom data type -- there is no deriving Show in Elm). First decode the string, then chain a pattern match using andThen:

data Transform = Rows | Cols | Boxes

transformDecoder : Decoder Transform
transformDecoder =
    Decode.string
    |> Decode.andThen
       (\ s ->
            case s of
                "Rows" -> Decode.succeed Rows
                "Cols" -> Decode.succeed Cols
                "Boxes" -> Decode.succeed Boxes
                _ -> Decode.fail <| "Unknown transformation.")