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

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

ncave picture ncave  路  3Comments

jwosty picture jwosty  路  3Comments

rommsen picture rommsen  路  3Comments

SirUppyPancakes picture SirUppyPancakes  路  3Comments