Fable: Deserializing F# Result doesn't restore type information

Created on 14 Dec 2017  路  4Comments  路  Source: fable-compiler/Fable

Description

This was reported by @vasily-kirichenko in Gitter.

When you try to deserialize the F# Result type from JSON using ofJson, it doesn't seem to restore the type of the data, because comparing the data with an object of the same type fails with "data.Equals is not a function".

Repro code

[<PassGenerics>]
let roundtrip x =
  x
  |> fun x -> JS.console.log (">", box x); x
  |> toJson
  |> ofJson
  |> fun x' -> JS.console.log ("<", box x'); x'

type FancyInteger = { N : int }

let Test () =
  let x = { N = 0 }
  let wx : Result<_, unit> = Ok x
  let wx' = roundtrip wx
  match wx' with
  | Ok x' ->
    if x' = x then
      ()
    else
      failwithf "Unexpected data %A" x'
  | res -> failwithf "Unexpected result %A" res

Expected and actual results

The given code fails at runtime with "matchValue.data.Equals is not a function". Before crashing, the code gives this output:

> Result { tag: 0, data: FancyInteger { N: 0 } }
< Result { tag: 0, data: { N: 0 } }

So it looks like the data loses its type information. Note that if I replace the Result type with a custom one (type MyResult<'a, 'b> = Ok of 'a | Error of 'b), the code gives the following output and passes:

> MyResult { tag: 0, data: FancyInteger { N: 0 } }
< MyResult { tag: 0, data: FancyInteger { N: 0 } }

Related information

  • Fable version (dotnet fable --version): 1.3.5, build from master

Most helpful comment

Awesome, thanks a lot for the fix @inosik! You 馃暫

The fix has been published in version 1.3.5 :+1:

All 4 comments

Yes, it's a very annoying bug, preventing passing Results to client.

I updated the code sample to use a record type. Too much "data" :smile:

The generated JS code for the Result type contains this:

    [FSymbol.reflection]() {
        return {
            type: "Microsoft.FSharp.Core.FSharpResult",
            interfaces: ["FSharpUnion", "System.IEquatable", "System.IComparable"],
            cases: [["Ok", Any], ["Error", Any]],
        };
    }

If I change cases to the following, it works:

cases: [["Ok", GenericParam("T")], ["Error", GenericParam("TError")]],

Awesome, thanks a lot for the fix @inosik! You 馃暫

The fix has been published in version 1.3.5 :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dbrattli picture dbrattli  路  54Comments

ncave picture ncave  路  35Comments

alfonsogarciacaro picture alfonsogarciacaro  路  30Comments

alfonsogarciacaro picture alfonsogarciacaro  路  37Comments

alexfoxgill picture alexfoxgill  路  39Comments