Fable: Simple trait calls not working properly

Created on 2 Aug 2020  路  6Comments  路  Source: fable-compiler/Fable

Here's two small repros. The former produces a compile error and the latter a runtime error.

type Map = Map with
    static member inline InvokeOnInstance (mapping: 'T->'U) (source: '``Functor<'T>``) : '``Functor<'U>`` = 
        (^``Functor<'T>`` : (static member Map : _ * _ -> _) source, mapping)

type Identity<'t> = Identity of 't with
    static member Map (Identity x, f : 'T->'U) = Identity (f x)  : Identity<'U>
    static member run (Identity x) = x  : 'T

let setl optic value (source: 's) : 't = Identity.run (optic (fun _ -> Identity value) source)

let mapItem2 f (x, y) = (x, f y)

let inline _2 f t = Map.InvokeOnInstance (fun x -> mapItem2 (fun _ -> x) t) (* <| *) (f (snd t))
// (non trait-call) -> let inline _2 f t = (fun f x -> Identity.Map(x,f)) (fun x -> mapItem2 (fun _ -> x) t) (f (snd t))

let r2 = setl _2 42 ("hello","world")
printfn "%A" r2

Uncommenting the <| operator solves the issue, no need to mention that this is not necessary in normal F#.

Of course, exchanging the problematic line with the following non-trait call also makes it work, but that's not interesting.

The next fragment comes from this issue in the repl https://github.com/fable-compiler/repl/issues/117

type First<'t> = First of Option<'t> with
    static member get_Zero () = First None : First<'t>
    static member run (First a) = a        : 't option

type Const<'t,'u> = Const of 't with
    static member inline Return (_: 'U) = Const LanguagePrimitives.GenericZero : Const<'T,'U>
    static member run (Const a) = a

let t2: int option =
    Error 1
    |> fun x ->  (^``Applicative<'T>`` : (static member Return : ^T -> ^``Applicative<'T>``) x)
    // (non trait-call) -> |> Const.Return
    |> Const.run 
    |> First.run

printfn "t2"

Here the situation is worst, as you can see in the linked issue it compiles fine but creates the wrong js code.

witnesses

All 6 comments

I'm getting compiler warnings when compiling these examples in .NET. For example, when compiling the second one:

warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'Result<'a,int>'.
warning FS1125: The instantiation of the generic type 'Const' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'Const<_,_>'.
warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'Applicative<'T> has been constrained to be type 'Const< ^a,Result<'b,int>>'.

Would it be possible to get a sample that doesn't generate compiler warnings?

Those warnings are not a big deal, if you want to get rid of them, just make them independent functions, here's how:

type First<'t> = First of Option<'t> with
    static member get_Zero () = First None : First<'t>

module First =
    let run (First a) = a        : 't option

type Const<'t,'u> = Const of 't with
    static member inline Return (_: 'U) = Const LanguagePrimitives.GenericZero : Const<'T,'U>

module Const =
    let run (Const a) = a


let inline ret x =  (^``Applicative<'T>`` : (static member Return : ^T -> ^``Applicative<'T>``) x)

let t2: int option =
    Error 1
    |> ret
    // (non trait-call) -> |> Const.Return
    |> Const.run 
    |> First.run

printfn "t2"

Thanks @gusty! With latest Fable build this code seems to compile correctly and doesn't generate errors as in the Fable 2 repl. I get the same result as in .NET (t2 is None), does the generate look right? Would it be possible to turn this into a test with an assertion?

export class First$1 extends Union {
    constructor(tag, ...fields) {
        super();
        this.tag = (tag | 0);
        this.fields = fields;
    }
    cases() {
        return ["First"];
    }
}

export function First$1$reflection(gen0) {
    return union_type("QuickTest.First`1", [gen0], First$1, () => [[["Item", option_type(gen0)]]]);
}

export function First$1_get_Zero() {
    return new First$1(0, void 0);
}

export function First_run(_arg1) {
    const a = _arg1.fields[0];
    return a;
}

export class Const$2 extends Union {
    constructor(tag, ...fields) {
        super();
        this.tag = (tag | 0);
        this.fields = fields;
    }
    cases() {
        return ["Const"];
    }
}

export function Const$2$reflection(gen0, gen1) {
    return union_type("QuickTest.Const`2", [gen0, gen1], Const$2, () => [[["Item", gen0]]]);
}

export function Const_run(_arg1) {
    const a = _arg1.fields[0];
    return a;
}

export const t2 = (() => {
    let arg00$0040_1;
    let arg00$0040;
    arg00$0040 = (new Const$2(0, First$1_get_Zero()));
    arg00$0040_1 = Const_run(arg00$0040);
    return First_run(arg00$0040_1);
})();

(function () {
    const clo1 = toConsole(printf("t2 %A"));
    clo1(t2);
})();

Yes, but this is an over simplified fragment of F#+ lensing.
If you're going to include F#+ tests it would be much better test than this.

The first sample compiles now. I don't know if the produced JS is correct. The second sample produces a different JS output than initially reported.

Regarding the second sample, its first version (the one with the warnings) still doesn't work (Fable 3.2.6), it doesn't print anything.
This is not a big deal, but it's an indicator that still there's a bug somewhere.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nozzlegear picture nozzlegear  路  3Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

MangelMaxime picture MangelMaxime  路  3Comments

MangelMaxime picture MangelMaxime  路  3Comments

SirUppyPancakes picture SirUppyPancakes  路  3Comments