Mark Probst
Mark Probst

Reputation: 7367

Decoding JSON into a non-alias type

I'm trying to decode directly or indirectly recursive types in Elm, which requires that at least one of them is defined as a "proper", non-alias, type. I'm using Json.Decode.Pipeline. Here's how I would do it with an alias, which obviously doesn't work:

import Json.Decode as Jdec
import Json.Decode.Pipeline as Jpipe

type alias List =
    { next : Maybe List
    , item : Float
    }

list : Jdec.Decoder List
list =
    Jpipe.decode List
        |> Jpipe.optional "next" (Jdec.nullable list) Nothing
        |> Jpipe.required "item" Jdec.float

How do I make the decoding function work if List is properly defined as

type List = List
    { next : Maybe List
    , item : Float
    }

Upvotes: 3

Views: 178

Answers (1)

Alex Lew
Alex Lew

Reputation: 2134

If you define your recursive type as you suggest:

type List = List
    { next : Maybe List
    , item : Float
    }

then you still need to overcome two obstacles in writing your decoder.

The first is that List is no longer the name of a type alias, and therefore, is no longer a function of two arguments that creates a list. Instead, List is a constructor, taking in one argument of type {next : Maybe List, item : Float}.

The second is that you will need to refer to list, the decoder, in the definition of list, which is flagged by the compiler as "bad recursion."

The solution to the first problem is to create your own list-making function: (\next item -> List {next=next, item=item}). The solution to the second problem is to use Json.Decode.lazy, which lets you use a decoder-returning closure in the place of a decoder:

list : Jdec.Decoder List
list =
    Jdec.map2 (\next item -> List {next=next, item=item})
          (Jdec.field "next" (Jdec.lazy (\_ -> Jdec.nullable list)))
          (Jdec.field "item" Jdec.float)

Upvotes: 4

Related Questions