Suppose I have a simple JSON decoder for integers and lists of integers:
import Json.Decode as JD
type MyType
= Number Int
| Numbers (List MyType)
myDecoder : JD.Decoder MyType
myDecoder =
JD.oneOf
[ JD.map Number JD.int
, JD.map Numbers (JD.lazy (\_ -> myDecoder) |> JD.list)
]
Although myDecoder
is a value with a recursive definition, this works fine because the recursive call is lazy. So I can decode nicely with, for example, JD.decodeString myDecoder "[1,2,3]"
What mystifies me is that I cannot move myDecoder
inline. In other words, the following will not compile, generating a ‘value is defined directly in terms of itself’ error:
testDecoder : Result JD.Error MyType
testDecoder =
let
myDecoder =
JD.oneOf
[ JD.map Number JD.int
, JD.map Numbers (JD.lazy (\_ -> myDecoder) |> JD.list)
]
in
JD.decodeString myDecoder "[1,2,3]"
Why does does the compiler allow top-level definition of the recursive value, but not an inline version?
For context, this came up when I was trying to create a general purpose JSON decoder and, thanks to help on Slack, was able to do so inline by making the decoder a unit function:
jsonToValue : String -> Result JD.Error JE.Value
jsonToValue =
let
jsDecoder () =
JD.oneOf
[ JD.map JE.string JD.string
, JD.map JE.int JD.int
, JD.map JE.float JD.float
, JD.map JE.bool JD.bool
, JD.map (JE.list identity) (JD.lazy jsDecoder |> JD.list)
, JD.map (Dict.toList >> JE.object) (JD.lazy jsDecoder |> JD.dict)
, JD.null JE.null
]
in
JD.decodeString (jsDecoder ())
Nevertheless, I remain curious why the original value specification cannot be declared inline.