Skip to content

Wrapping a constructor parameter with Option.Some before setting an optional field throws method not supported error. #366

Open
@bdkoepke

Description

@bdkoepke

Description

I'm building a type provider for Bloomberg (they have xml based schema files that describe the entire API). The code-generation part of the API is pretty close to working (there are four primary different types: enums, records, unions, and operations). For the implementation of the record type and union types, I'm trying to improve the API so 'optional' parameters can be specified as the value directly rather than (Some t'). When I change the code from the following:

es
|> Result.mapError (fun xs -> ChoiceError.Element(c.Name, xs))
|> Result.map (fun xs ->
    xs
    |> List.iter (fun (_, field) ->
        let parameter = ProvidedParameter(field.Name, field.FieldType)
        providedChoiceType.AddMember
        <| ProvidedConstructor(
            [parameter],
            invokeCode =
                fun args ->
                    match args with
                    | this :: [arg] ->
                        let setValue = Expr.FieldSet(this, field, arg)
                        let enumField = Expr.FieldSet(this, enumField, Expr.FieldGet(enum.GetField(field.Name)))
                        Expr.Sequential(enumField, setValue)
                    | _ -> failwith "wrong ctor parameters"))
    {
        Enum = enum
        Object = providedChoiceType
    }))

to

es
|> Result.mapError (fun xs -> ChoiceError.Element(c.Name, xs))
|> Result.map (fun xs ->
    xs
    |> List.iter (fun (type', field) ->
        let parameter = ProvidedParameter(field.Name, type')
        providedChoiceType.AddMember
        <| ProvidedConstructor(
            [parameter],
            invokeCode =
                fun args ->
                    match args with
                    | this :: [arg] ->
                        let some =
                            FSharpType.GetUnionCases(field.FieldType)
                            |> Array.filter (fun x -> x.Name = "Some")
                            |> Array.exactlyOne
                        let arg' = Expr.NewUnionCase(some, arg)
                        let setValue = Expr.FieldSet(this, field, arg')
                        let enumField = Expr.FieldSet(this, enumField, Expr.FieldGet(enum.GetField(field.Name)))
                        Expr.Sequential(enumField, setValue)
                    | _ -> failwith "wrong ctor parameters"))
    {
        Enum = enum
        Object = providedChoiceType
    }))

Then I get the error "Specified method is not supported."

The key part is really just the following:

from

 let setValue = Expr.FieldSet(this, field, arg)

to

let some =
    FSharpType.GetUnionCases(field.FieldType)
    |> Array.filter (fun x -> x.Name = "Some")
    |> Array.exactlyOne
let arg' = Expr.NewUnionCase(some, arg)
let setValue = Expr.FieldSet(this, field, arg')

Wrapping the argument in 'Some' seems to be causing this error.

Expected behavior

I'm trying to generate a type like the following:

type Example(x: int) as self =
    [<DefaultValue>] val mutable X: option<int>

    do
        self.X <- Some x

    member this.GetX with get() = self.X

Such that the argument is not an "optional" type but the backing field is.

Actual behavior

I get the error "Specified method is not supported."

image

Known workarounds

I have this working:

type Example(x: option<int>) as self =
    [<DefaultValue>] val mutable X: option<int>

    do
        self.X <- x

    member this.GetX with get() = self.X

But that sucks because now you can only instantiate the Example object by doing:

Example(Some 1)

Rather than

Example(1)

Thank you!

Related information

dotnet 3.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions