Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird value returned after (maybe failed) overload resolution #155

Open
gusty opened this issue Jul 16, 2022 · 3 comments
Open

Weird value returned after (maybe failed) overload resolution #155

gusty opened this issue Jul 16, 2022 · 3 comments

Comments

@gusty
Copy link

gusty commented Jul 16, 2022

This bug is not about taking the wrong overload, which is an ongoing problem not easy to solve, but about returning a value not corresponding to any of the overloads.

I'm under the impression that overload resolution fails and then some buggy code returns a no sense value instead of a compile error.

Here's a repro


open System

type Default6 = class end
type Default5 = class inherit Default6 end
type Default4 = class inherit Default5 end
type Default3 = class inherit Default4 end
type Default2 = class inherit Default3 end
type Default1 = class inherit Default2 end

type [<Struct>]Result2<'T, 'E> = Ok2 of OkField:'T | Error2 of ErrorField:'E

type Bind =
    static member (>>=) (source: Lazy<'T>   , f: 'T -> Lazy<'U>    ) = lazy (f source.Value).Value    : Lazy<'U>
    static member (>>=) (source: seq<'T>    , f: 'T -> seq<'U>     ) = Seq.collect f source           : seq<'U>
    static member (>>=) (source             , f: 'T -> _           ) = Option.bind   f source         : option<'U>
    static member (>>=) (source             , f: 'T -> _           ) = List.collect  f source         : list<'U>
    static member (>>=) (source             , f: 'T -> _           ) = Array.collect f source         : 'U []
    static member (>>=) (source             , f: 'T -> _           ) = async.Bind (source, f)         : Async<'U>
    static member (>>=) (source             , k: 'T -> _           ) = Result.bind k source           : Result<'U,'E>
    static member (>>=) (source: Result2<'T,'E>   , k: 'T -> Result2<'U,'E>) = Unchecked.defaultof<_> : Result2<'U,'E>

    static member inline Invoke (source: '``Monad<'T>``) (binder: 'T -> '``Monad<'U>``) : '``Monad<'U>`` =
        let inline call (_mthd: 'M, input: 'I, _output: 'R, f) = ((^M or ^I or ^R) : (static member (>>=) : _*_ -> _) input, f)
        call (Unchecked.defaultof<Bind>, source, Unchecked.defaultof<'``Monad<'U>``>, binder)


type Return =
    inherit Default1

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

    static member inline Invoke (x: 'T) : '``Applicative<'T>`` =
        let inline call (mthd: ^M, output: ^R) = ((^M or ^R) : (static member Return : _*_ -> _) output, mthd)
        call (Unchecked.defaultof<Return>, Unchecked.defaultof<'``Applicative<'T>``>) x    

    static member        Return (_: seq<'a>        , _: Default2) = fun  x      -> Seq.singleton x : seq<'a>
    static member inline Return (_: 'R             , _: Default1) = fun (x: 'T) -> Return.InvokeOnInstance x      : 'R
    static member        Return (_: Lazy<'a>       , _: Return  ) = fun x -> Lazy<_>.CreateFromValue x : Lazy<'a>
    static member        Return (_: option<'a>     , _: Return  ) = fun x -> Some x                               : option<'a>
    static member        Return (_: list<'a>       , _: Return  ) = fun x -> [ x ]                                : list<'a>
    static member        Return (_: 'a []          , _: Return  ) = fun x -> [|x|]                                : 'a []
    static member        Return (_: 'a Async       , _: Return  ) = fun (x: 'a) -> async.Return x
    static member        Return (_: Result<'a,'e>  , _: Return  ) = fun x -> Ok x                                 : Result<'a,'e> 
    static member        Return (_: ResizeArray<'a>, _: Return  ) = fun x -> ResizeArray<'a> (Seq.singleton x)


type Delay =
    inherit Default1

    static member inline Delay (_mthd: Default3, x: unit-> ^``Monad<'T>`` when ^``Monad<'T>`` : struct     , _: Default2) = Bind.Invoke (Return.Invoke ()) x : ^``Monad<'T>``
    static member inline Delay (_mthd: Default4, x: unit-> ^``Monad<'T>`` when ^``Monad<'T>`` : not struct , _: Default1) = Bind.Invoke (Return.Invoke ()) x : ^``Monad<'T>``

    static member        Delay (_mthd: Default2, x: unit-> _                                              , _          ) = Seq.delay x      : seq<'T>
    static member        Delay (_mthd: Default2, x: unit-> 'R -> _                                        , _          ) = (fun s -> x () s): 'R -> _

    static member inline Invoke (source : unit -> '``Monad<'T>``) : '``Monad<'T>`` =
        let inline call (mthd: ^M, input: unit -> ^I) = ((^M or ^I) : (static member Delay : _*_*_ -> _) mthd, input, Unchecked.defaultof<Delay>)
        call (Unchecked.defaultof<Delay>, source)


let res = Delay.Invoke (fun () -> [5] )
printfn "res is %A" res

Running this prints res is [object Object], but if we comment out the first overload of Delay, we get res is [5] which is the expected result.

Not sure, but it seems to be a problem when there are constraints in the overload, like not struct.

@gusty gusty changed the title Weird value return after (maybe failed) overload resultion Weird value returned after (maybe failed) overload resolution Jul 16, 2022
@alfonsogarciacaro
Copy link
Member

alfonsogarciacaro commented Jul 20, 2022

Hi @gusty! Thanks a lot for the report and providing the code to reproduce. This is interesting, when debugging the sample I can see the F# compiler does provide witnesses for resolution so Fable's own mechanism doesn't come into play here. It could be an issue with Fable when assigning the witness, but right now it looks as if FCS resolves the witness differently for .NET compilation and when creating the AST 😕

If you change the first argument in the second Delay method to Default3 (or Default2 or Default1) then it works.

    static member inline Delay (_mthd: Default3, x: unit-> ^``Monad<'T>`` when ^``Monad<'T>`` : struct     , _: Default2) = Bind.Invoke (Return.Invoke ()) x : ^``Monad<'T>``
    static member inline Delay (_mthd: Default3, x: unit-> ^``Monad<'T>`` when ^``Monad<'T>`` : not struct , _: Default1) = Bind.Invoke (Return.Invoke ()) x : ^``Monad<'T>``

@gusty
Copy link
Author

gusty commented Jul 20, 2022

Thanks for the workaround and interesting trace into the overload resolution problem, however the goal of this repro was to understand why Fable returns this object value.
Where is it coming from?
None of these overloads would return such value.

@alfonsogarciacaro
Copy link
Member

Ah, sorry, didn't read well the description. The returned object is the lazy value, it looks like indeed is picking the wrong overload. The problem here is we haven't implemented ToString for lazy objects so you just see the default stringification for JS objects. We should fix that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants