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) ]
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.