Search code examples
reflectionf#funscript

F# Expr.TryGetReflectedDefinition with overloaded methods


I'm writing some extensions for the FunScript project (F# to Javascript compiler). If you're interested you can find the source here.

I was trying to emulate the AwaitObservable extension by Tomas Petricek. However, if I use overloaded methods like AwaitObservable(w), AwaitObservable(w1,w2)... I get the following error when I try to compile a project to Javascript:

An unhandled exception of type 'System.Reflection.AmbiguousMatchException' occurred in FSharp.Core.dll

Additional information: Ambiguous match found.

FunScript keeps a cache dictionary of the reflected definitions in the project, and this error occurs when it tries to add a new one to the cache using Expr.TryGetReflectedDefinition. The error disappears if I use different names instead of overloads (AwaitObservable2, AwaitObservable3...). That's the workaround I'm using now, but I would like to know more about the problem and whether is possible to fix it so the users of the extensions can use overloaded methods normally.

I can imagine that Reflected Definitions in F# don't support overloaded methods and cannot distinguish methods just by the number of arguments (I couldn't check that because I didn't find the implementation of Expr.TryGetReflectedDefinition in the fsharp GitHub source repository). However, what puzzles me is that the error doesn't happen when looking for AwaitObservable but the following method:

{System.IDisposable System-IObservable1-Subscribe(System.IObserver1[FunScript.TypeScript.MouseEvent])} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}

So my questions are:

  1. Why is Expr.TryGetReflectedDefinition failing with IObservable.Subscribe instead of Async.AwaitObservable?
  2. Why is Expr.TryGetReflectedDefinition throwing an exception instead of returning None?
  3. Is this a bug of F# Reflected Definitions or an unavoidable limitation? Is it possible to fix it?

Thanks a lot in advance for your help!


Solution

  • Ok, it seems this has no easy answer so I'll try to reply my own question. After further investigation...

    1. It seems that because of the Async.AwaitObservable overloads the stored Reflected Definitions somehow (this 'somehow' is what remains unexplained) lose track of the type implementing IObservable, and that's why they do not know which implementation of Subscribe should be used.
    2. I guess this is intended behaviour: returning None means the Reflected Definition has not been found, but here there was an ambiguous match so it's a different case.
    3. I don't know yet if this is a bug or a 'feature', but in any case I fixed the problem in this particular situation using the CompiledName attribute and giving a different name to each overload. Now everything works fine :)

    I hope next time I post a question that can actually be answered ;) Thanks to everybody who took the time to read this.