3

I'm trying to build my first toy-like Type Provider. What I'm trying to achieve is to have dynamically generated properties of dynamically generated types.

collection 
|> getItems
|> Seq.map(fun mapItem ->            
    let nestedType = ProvidedTypeDefinition(assembly, ns, "MyNestedType", None)
    let ctor = ProvidedConstructor(List.Empty)
    nestedType.AddMember ctor
    mapItem.Value 
        |> Seq.map(fun pair -> 
        ProvidedProperty(fst(pair), typeof<string>,
            GetterCode = fun [_] -> <@@ snd(pair) @@>))
        |> Seq.toList
        |> nestedType.AddMembers


    ProvidedProperty(mapItem.Key, nestedType,
        GetterCode = fun [map] ->   
            // ?? Runtime Exception   
            let inst = nestedType.GetConstructors().[0].Invoke([||])             
            <@@ inst @@>
                ))
    |> Seq.toList
    |> ty.AddMembers      

ty

How should I instantiate dynamically generated type ?

FoggyFinder
  • 2,230
  • 2
  • 20
  • 34
mickl
  • 48,568
  • 9
  • 60
  • 89

2 Answers2

3

I'm assuming this is an erasing type provider (those are the easy ones, so they're better choice for getting started). If that's not the case, then disregard my answer.

In the GetterCode, you do not need to create instance of the nested provided type. You just need to create an instance of the type that it is erased to.

In your case, nestedType is erased to None and so the constructor just needs to create a System.Object value, so you should be able to use:

ProvidedProperty(mapItem.Key, nestedType,
    GetterCode = fun [self] -> <@@ obj() @@>)

In reality, you'll probably want to erase to some type that lets you keep some data that the nested type is supposed to access. If the nested type was erased to, say, MyRuntimeType, you could then write:

let parameter = mapItem.WhateverYouWantHere
ProvidedProperty(mapItem.Key, nestedType,
    GetterCode = fun [self] -> <@@ MyRuntimeType(parameter) @@>)

Note that I'm using let to capture the value of the primitive parameter type, so that the compiler can serialize the quotation (you cannot capture complex object types in a quotation).

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Thanks, I understand now how erased type works. The problem is that I don't know names of properties at compile time of type provider, so if I'm not wrong I am able to erase only to object. – mickl Apr 22 '16 at 20:30
  • What I'm trying to achive is only to have something like this: providedType.dynamically_generated_prop.dynamically_generated_prop_nested, when consuming provider – mickl Apr 22 '16 at 20:37
0

What you're trying to do here is instantiate your type while building the provider, and then include that new instance in the body of the property. It should be abundantly clear that you can't instantiate the provided type before you've finished providing it.

What you really want to do is take your provided constructor and build a quotation that calls it. You can't have the compiler build the quotation for you, because in order for the compiler to compile the body of the quotation, it needs to "see" all types/methods/functions inside, and your type is not yet ready. But you can create the quotation manually by using the various constructors under Quotations.Expr. In this case, NewObject is suitable:

    GetterCode = fun [map] -> Expr.NewObject (ctor, [])
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172