Fable: Different representations of Option.None as a function argument

Created on 6 Jul 2016  路  9Comments  路  Source: fable-compiler/Fable

Description

Option.None value can be represented in JavaScript as null or undefined, depending on the way it is passed to a function. As a result, values that are equal in F# are not equal in JavaScript.

Repro steps

Following two tests reproduce this issue:

type OptTest = OptTest of int option

[<Test>]
let ``Different ways of providing None to a union case should be equal``() =                      
    let value = None
    equal true ((OptTest None) = (value |> OptTest))    

[<Test>]
let ``Different ways of providing None to a function should be equal``() =        
    let f x = x
    let value = None

    equal true ((f None) = (value |> f))    

Expected behavior

Both tests should pass in F# and in JavaScript.

Actual behavior

Both tests fail in JavaScript.

Known workarounds

Don't pass Option.None value to a function directly, use a variable instead.

Related information

  • Windows 10 64bit
  • master branch
bug

All 9 comments

This is my only fault ;) I was removing null arguments in function calls (mainly for aesthetic reasons to comply with our _JS you can be proud of_ motto) but this can cause conflicts as you've noticed so I've removed the removing (pun intended).

Thanks a lot for reporting and providing the tests!

Unfortunately my fix was causing some problems with other programs. Option types are used to represent optional arguments in TypeScript definitions and some methods failed when passed null to represent None, like CanvasRenderingContext2D.fill: ?fillRule: string -> unit. I had to revert the fix. Another solution is needed 馃槙

Instead of trying to solve this problem with nulls, what about creating a "real" Option type like what was done with Choice? Simple. And gets the job done.

One of the beauties of F# is that an int option is a completely different beast than an int. Not just "an int?".

If you are worried with the performance..., let's make it _right_. THEN, make it _faster_ ;)

There's a discussion about this here. My main worry is that option is used to represent Typescript optional members which can be undefined. If we make option a _true F# option_ it will make this optional member unrepresentable. Also, the problem above would still happen: option is also used to represent TypeScript optional arguments, if we pass anything else than a fillRule to CanvasRenderingContext2D.fill the method will still fail.

I think erasing option when compiling to JS is fairly sensible. Idiomatic way of representing missing things in F# is None and idiomatic way of representing missing things in JS is null, so mapping between them is quite natural (provided that it can work in 99% cases).

Probably the best way to fix this specific issue is to use non-strict equality when comparing optional types. This will identify null and undefined (JS equality rules, aren't they to be loved?) and the F# will already prevent us from comparing option int against option string so we should be safe from the infamous 1 == "1" 馃槈

I see your points, guys. A fairly sensible choice, indeed ;) :+1:

There is another possibility of errors due to different representations of Option type.
For example, the method CanvasRenderingContext2D.fill: ?fillRule: string -> unit,
can fail when called like this:

let (v: string option) = None
ctx.fill(?fillRule = None)    // OK
ctx.fill(?fillRule = v)           // fails

But this is a contrived example, so I guess it doesn't need to be addressed at all.

Yeah, that one is gonna be difficult to fix, let's just hope nobody tries to do something like this ;)

Hopefully I've fixed the major problem though I have to change a bit the way union cases are constructed (now you need to pass the fields' length too). Please feel free to reopen the issue if it's still not working for you.

Cheers!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alfonsogarciacaro picture alfonsogarciacaro  路  28Comments

bilkusg picture bilkusg  路  28Comments

et1975 picture et1975  路  43Comments

alfonsogarciacaro picture alfonsogarciacaro  路  57Comments

ed-ilyin picture ed-ilyin  路  48Comments