FSharp.TypeProviders.SDK icon indicating copy to clipboard operation
FSharp.TypeProviders.SDK copied to clipboard

Can not add generic interface onto ProvidedTypeDefinition

Open mvkara opened this issue 9 years ago • 3 comments

Adding an interface to a generated type doesn't work; particuarly in the case where it is an existing interface that has generic arguments that are provided types. This is a common use case for us since I want to generate types that have implementation of IComparable<GeneratedType>, IEquatable<GeneratedType>, etc. Looking at the ProvidedTypes.fs code I'm not quite sure about the best way to fix this without breaking something else. If anyone can give me some guidance about either fixing it or working around it as well would appreciate it.

To reproduce:

Create a generative type provide that creates a parent type to add all your types to then use the following method to get a list of types to add to it:

let buildType() = 
    let providedType1 = ProvidedTypeDefinition("ParentType", Some typeof<obj>, IsErased = false)
    let providedType2 = ProvidedTypeDefinition("DataType", Some typeof<obj>, IsErased = false)

    let symbolType = ProvidedTypeBuilder.MakeGenericType(typedefof<IEnumerable<_>>, [providedType2])

    let providedField = ProvidedField("propertyField", symbolType)
    providedType1.AddMember(providedField)

    let providedProperty = ProvidedProperty("TestProperty", symbolType)
    providedProperty.GetterCode <- fun args -> Expr.FieldGet(args.[0], providedField)
    providedType1.AddMember providedProperty

    let pconstructor = ProvidedConstructor([ProvidedParameter("dataList", symbolType)])
    pconstructor.InvokeCode <- fun args -> <@@ Expr.FieldSet(args.[0], providedField, args.[1]) @@>
    providedType1.AddMember pconstructor

    let nestedProperty = ProvidedProperty("Data", typeof<string>)
    nestedProperty.GetterCode <- fun _ -> <@@ "TEST SUCCESS" @@>
    providedType2.AddMember nestedProperty

    let interfaceType = ProvidedTypeBuilder.MakeGenericType(typedefof<IEquatable<_>>, [providedType2])
    providedType2.AddInterfaceImplementation interfaceType

    let equalsParameter = ProvidedParameter("other", providedType2)
    let providedMethodEquals = ProvidedMethod("Equals", [ equalsParameter ], typeof<bool>)
    providedMethodEquals.InvokeCode <- fun args ->
        let propertyGet x = Expr.PropertyGet(x, nestedProperty)
        let currentEq = propertyGet args.[0]
        let otherEq = propertyGet args.[1]
        <@@ %%currentEq = %%otherEq @@>

    // Add these to the generated type with a namespace outside this method
    [providedType1; providedType2 ]`

Then it is as simple as trying to use the Generative Type Provider you have created.

What I expect is that the generated type is useable and has both the members for the interface added and properly extends the interface with another generated type as its generic argument. What I get unfortunately is exceptions similar to the following: "The operation 'Module' on item 'IEquatable<DataType>' should not be called on provided type, member or parameter"

Unfortunately I don't see workarounds for the issue.

Tested under Mono 4.2.3 and Windows 7 with Visual Studio 2015.

mvkara avatar May 13 '16 22:05 mvkara

@mvkra Did you ever resolve this?

7sharp9 avatar Apr 13 '17 12:04 7sharp9

No never resolved this - ended up using a different clunkier approach if I recall (other than TP's) which is a shame. Better late reply than never.

mvkara avatar Jan 09 '18 22:01 mvkara

What's the recommended approach to generating comparison and equality on provided types?

panesofglass avatar Mar 13 '18 22:03 panesofglass