Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get F# quotation for a interface instance method?

Tags:

f#

quotations

In F#, we can create interface instance by object expression, but while I'm trying to use attribute ReflectedDefinition on the instance method, then I cannot get the quotations. The method info is declared in the interface type, not the instance type.

Here is my test code:

module Test

open System
open System.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape

type IMyInterface =
    abstract Foo : int -> int

let createMyInterface () =
    { new IMyInterface with
        [<ReflectedDefinition>]
        member this.Foo a = a + 1 }

let expr =
    let a = createMyInterface()
    <@ a.Foo(42) @>

let rec iterExpr (expr:Expr) =
    match expr with
    | Call(objectExpr, info, paramExprs) ->
        printfn "info: %A" info
        printfn "reflected type: %A" info.ReflectedType
        match info with
        | MethodWithReflectedDefinition methodExpr ->
            printfn "%A" methodExpr
        | _ -> failwith "No reflected definition"

    | ShapeVar _ -> failwithf "TODO: %A" expr
    | ShapeLambda _ -> failwithf "TODO: %A" expr
    | ShapeCombination _ -> failwithf "TODO: %A" expr

let test() =
    iterExpr expr

[<EntryPoint>]
let main argv = 
    test()
    0 // return an integer exit code

If I run it, I got exception:

C:\Users\Xiang\Documents\Inbox\TTTT\bin\Debug>TTTT
info: Int32 Foo(Int32)
reflected type: Test+IMyInterface

Unhandled Exception: System.Exception: No reflected definition
   at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
   at Test.iterExpr(FSharpExpr expr) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 30
   at Test.test() in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 37
   at Test.main(String[] argv) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 41

And I also checked the generated assembly with dotPeek, it is implemented as a derived class:

[CompilationMapping(SourceConstructFlags.ObjectType)]
  [Serializable]
  public interface IMyInterface
  {
    int Foo([In] int obj0);
  }

  [CompilationMapping(SourceConstructFlags.Closure)]
  [Serializable]
  [SpecialName]
  [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
  internal sealed class createMyInterface\u004014 : Test.IMyInterface
  {
    public createMyInterface\u004014()
    {
      base.\u002Ector();
      Test.createMyInterface\u004014 createMyInterface14 = this;
    }

    [ReflectedDefinition]
    int Test.IMyInterface.Test\u002DIMyInterface\u002DFoo([In] int obj0)
    {
      return obj0 + 1;
    }
  }

So, the problem is, when I call the Foo method in quotation, the Call pattern get MethodInfo which is declared at interface type, which has no definition. So how could I get the actually implementation MethodInfo? and then I can get the quotation of the implementation?

like image 219
Xiang Zhang Avatar asked Apr 17 '26 18:04

Xiang Zhang


1 Answers

Here's your problem in a nutshell:

  1. You're calling a virtual method through an instance of the type where the method is defined.
  2. You want the quotation to contain a call to the method as defined on the derived class.

This won't work, and isn't limited to interfaces or object expressions:

type A() = 
    abstract M : unit -> unit
    default this.M() = printfn "abstract"

type T() =
    inherit A() with
        [<ReflectedDefinition>]
        override this.M() = printfn "override"

let expr =
    let a : A = upcast T()
    <@ a.M() @>

Fundamentally, the whole point of an object expression is to provide an anonymous implementation of a non-sealed class, so what you're asking for doesn't make sense to me - the compiler only knows that the object is some instance implementing that interface but can't know the concrete type of the instance and therefore can't know which (of potentially many) concrete method to use.

like image 68
kvb Avatar answered Apr 20 '26 10:04

kvb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!