3
votes

I'm trying to write a generic json decode function in fable. It seems to compile in FSharp but I get the error message for this code:

[using the Thoth.Json library and the Fetch library from Fable.PowerPack]

let autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
    let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
    let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str) 
    Result.mapError getDecoderError tryDecode

error FABLE: Cannot get type info of generic parameter, please inline or inject a type resolver

I'm not sure how to fix this and haven't been able to find anything on google.

I want to be able to call the function like this in my update function in Fable Elmish:

let update (msg:Msg) (model:Model) =
    match msg with
..
    | OrStart ->
        let getData() = Fetch.fetchAs<ResultResponse>  "https://randomuser.me/api/" json.autoDecoder<ResultResponse> http.getHeaders
        model, Cmd.ofPromise getData () LoadedTypedData FetchError

How can I get fable to compile the autoDecoder<'a> function while keeping it generic?

Thanks

2

2 Answers

2
votes

I think Fable is telling you to use inline like this:

let inline autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
    let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
    let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str) 
    Result.mapError getDecoderError tryDecode

That is because generic functions just like inline functions need to be instantiated for each call.

BTW, the value parameter is not being used.

You can also streamline the code like this:

let inline autoDecoder<'a> (json:string) : Result<'a, Thoth.Json.Decode.DecoderError> =
    Thoth.Json.Decode.Auto.fromString<'a> json
    |> Result.mapError (fun (str:string) ->  "Auto decode Error", Thoth.Json.Decode.FailMessage str) 
1
votes

I'm new to fable and I wasn't able to get it working, the fable compiler does not allow the auto decode without a specified type - fails here:

Thoth.Json.Decode.Auto.fromString<'a>(str, true)

But for anyone struggling with the fetch api in fable, this can be done with not too much boilerplate code. I couldn't get the promise to be generic, but a type specific implementation like getCustomers is quite succinct and I ended up doing something like this:

type Msg =
    | Start
    | LoadedCustomerData of Result<QueryDataForJson, string>
..

let getCustomers () = promise {
    let! response = Fetch.fetch "http://localhost:5000/spa/api/customers" http.getHeaders
    let! text = response.text()
    return Thoth.Json.Decode.Auto.fromString<QueryDataForJson>(text, true)
}
..
let update (msg:Msg) (model:Model) =
    match msg with
    | Start ->
        model, Cmd.ofPromise getCustomers () LoadedCustomerData FetchError
    | LoadedCustomerData resp ->
        match resp with
            | Ok qdj -> { model with gridData= queryDataFromJson qdj; message= "Loaded Customer Data"  }, Cmd.none
            | Error str -> { model with message = str }, Cmd.none