Reputation: 7367
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
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