Search code examples
f#type-providersquotations

How to call a function from the quotation of a invoke in type provider?


I have a type provider which gives me the error "Type mismatch when splicing expression into quotation literal".

I extracted the code below in order to reproduce the issue in a smaller context.

let f (s : string) : string = s //some dummy implementation

let t = ProvidedTypeDefinition(asm, ns, "Root", Some typeof<obj>)
let ctor = ProvidedConstructor(parameters = [],
                               InvokeCode = (fun args -> <@@ "root" :> obj @@>)) :> MemberInfo

let prop = ProvidedProperty(propertyName = "SomeProperty",
                            parameters = [],
                            propertyType = typeof<string>,
                            GetterCode = (fun args -> <@@ f %%(args.[0]) @@>)) :> MemberInfo

do  t.AddMembers [ctor; prop]
    t.SetBaseType typeof<obj>

...and when I use the type provider like

let root = Provided.Root()

let a = root.SomeProperty

I get the error:

Error: The type provider 'typeproviders.providerpoc+MyProvider' reported an error in the context of provided type 'typeproviders.providerpoc.Provided.Root', member 'get_Menu'.

The error: Type mismatch when splicing expression into quotation literal.

The type of the expression tree being inserted doesn't match the type expected by the splicing operation.

Expected 'System.Object', but received type 'System.String'.

Consider type-annotating with the expected expression type, e.g., (%% x : string) or (%x : string).. Parameter name: receivedType.

How can I write the quotation to be able to call a function inside the quotation?

Thanks!


Solution

  • What the error message is saying is that you are putting a quoted expression of type obj in a place where a quoted expression of type string is expected.

    I suspect this is happening when creating the GetterCode in the provided property:

    GetterCode = (fun args -> <@@ f %%(args.[0]) @@>)
    

    Here, args is an array of quoted expressions where each expression is of type obj, but the function f expects a string and so the quotation filling the hole using %% should be of type string

    Adding a type conversion that would turn the obj into string should do the trick:

    GetterCode = (fun args -> <@@ f (unbox<string> (%%(args.[0]))) @@>)