3
votes

Is it possible to write a Generated Type Provider that provides types equivalent to the following F# code?

[<ProvidedTypeFlag("myTypeA")>]
type A(x:int) =
    inherit ValueType(x)
    member __.X = x+1

[<ProvidedTypeFlag("myTypeB")>]
type B(value:ValueType) =
    member __.Raw = value
    member __.toA = A(value.X)

    interface IComparable with
        member this.CompareTo obj =
            match obj with
            | :? B as other -> this.Raw.X.CompareTo (other.Raw.X)
            | _             -> invalidArg "obj" "not a B"

[<ProvidedTypeFlag("myTypeC")>]
type C() =
    static member Process(a:A) =
        seq {
            for x in [1..a.X] do
                yield B(ValueType(x))
        } |> Set.ofSeq

assume that I have following types in the same assembly

// Value type that hold some data from 3rd party system
type ValueType (x:int) =
    member __.X = x

// Custom attribute that I want to have on provided types
[<AttributeUsage(AttributeTargets.Class, AllowMultiple=false)>]
type ProvidedTypeFlagAttribute(originName:string) =
   inherit System.Attribute()
   member __.OriginName = originName

If it is possible, please provide a sample of how to do it using ProvidedTypes.fs

1
Yes it's possible, I have similar ones that implement interfaces and auto disposing etc. - 7sharp9

1 Answers

2
votes

Looking at this it looks like you need several little pieces.

Add a custom attribute

I use a little helper like this:

type CustomAttributeDataExt =
    static member Make(ctorInfo, ?args, ?namedArgs) = 
        { new CustomAttributeData() with 
            member __.Constructor =  ctorInfo
            member __.ConstructorArguments = defaultArg args [||] :> IList<_>
            member __.NamedArguments = defaultArg namedArgs [||] :> IList<_> }

The optional args and namedArgs makes like a bit easier, and the code cleaner too. When I want to add a custom attribute, if there are several, I will normally add several more typed helpers to make things clearer in the code:

module Attributes =
let MakeActionAttributeData(argument:string) =
    CustomAttributeDataExt.Make(typeof<ActionAttribute>.GetConstructor(typeof<string>),
                                [| CustomAttributeTypedArgument(typeof<ActionAttribute>, argument) |])

open Attributes
myProperty.AddCustomAttribute <| Attributes.MakeActionAttributeData("attributeData")

Adding a Constructor that calls the base type

Again, I have some little helpers for reflection:

    type Type with
    member x.GetConstructor(typ) =
        x.GetConstructor([|typ|])

    member x.TryGetConstructor(typ:Type) =
        x.GetConstructor(typ) |> function null -> None | v -> Some v

    ...

create your providedType as usual (Remembering to set IsErased=false) , then

    //string ctor
    match providedType.TryGetConstructor(typeof<string>) with
    | None -> failwithf "No string constructor found for type: %s" providedType.Name
    | Some ctor -> let stringCtor = ProvidedConstructor([ProvidedParameter("theString", typeof<string>)], InvokeCode=Expr.emptyInvoke, BaseConstructorCall = fun args -> ctor, args)
                   providedController.AddMember(stringCtor)

I think there other parts should be documented elsewhere especially adding normal members etc.