使用F#生成类型提供程序类型作为泛型类型参数

时间:2014-02-28 03:55:11

标签: f# type-providers

背景

我正在学习生成型提供者。

我使用了来自herehere的Cameron Taggart的VectorTP示例。在该代码中,他为具有设计时指定数量的属性的向量类构建C#代码,编译它并返回生成的类型。它运作良好。

例如,此客户端代码编译并运行:

type Vector2D = Vector<"X", "Y">
let v1 = Vector2D()
v1.X <- 3.14
v1.Y <- 2.91

如果您在设计时查看类型提供程序代码内部的内容,您会看到类型提供程序代码的调用方式如下:

ITypeProvider.ApplyStaticArguments(
    VectorTP.Vector,                       // typeWithoutArguments
    [|"ConsoleApplication8"; "Vector2D"|], // typeNameWithArguments
    [|"X"; "Y"; ""; ""; ""; ""; ""|])      // staticArguments

一切看起来都不错。

问题

客户端代码编译:

type Vector2D = Vector<"X", "Y">
let list = System.Collections.Generic.List<Vector2D>()

这一次,如果您在设计时查看类型提供程序代码内部的内容,当List<Vector2D>添加到客户端代码时,您会看到此附加调用:

ITypeProvider.ApplyStaticArguments(
    Mindscape.Vectorama.Vector2D,   // typeWithoutArguments
    [|"Vector2D,"|],                // typeNameWithArguments
    [|""; ""; ""; ""; ""; ""; ""|]) // staticArguments

似乎类型提供程序框架(正确的术语?)正在调用ITypeProvider.ApplyStaticArguments,要求基于Vector2D的生成类型而没有任何静态参数。但是,Vector2D已经是生成的类型了?!

VectorTP示例没有正确处理这种情况,因此客户端代码将无法编译。

注意

我尝试将type Vector2D = Vector<"X", "Y">声明移动到单独的DLL中,然后引用该DLL。当然,这是按预期工作的。生成的Vector2D类在该点看起来就像任何其他类型。

复杂化似乎是生成类型并将其用作相同程序集中的通用参数(或脚本,通过我没有尝试过)。

问题

  • 这是“类型提供程序框架”中的问题吗?或者这是预期的行为?

  • 当我使用生成的类型作为泛型类型参数时,为什么要调用ApplyStaticArguments

  • 如果ITypeProvider应该处理这种情况,那么正确的答案是什么?

1 个答案:

答案 0 :(得分:7)

阅读完评论并做更多实验后,这是我的结论。

<强> 1。这是“类型提供程序框架”中的问题吗?或者这是预期的行为?

这不是期望的行为。

当您尝试将生成的类型用作泛型类型参数时,问题才会变得明显。当您将生成的类型用作泛型类型参数时,框架会为该生成的类型调用ITypeProvider.GetStaticParameters。我还不清楚为什么需要这样做。

无论如何,由于这个意外的调用,ITypeProvider.GetStaticParameters()的实现不能像这样简单:

member this.GetStaticParameters(typeWithoutArguments) =
    [1..7] |> List.map (fun i -> stringParameter i "") |> List.toArray

必须是这样的:

member this.GetStaticParameters(typeWithoutArguments) =
    if typeWithoutArguments = typeof<Vector> then
        [1..7] |> List.map (fun i -> stringParameter i "") |> List.toArray
    else
        [||] // for the generated types like Vector2D

完成上述更改后,我能够编译使用List<Vector2D>的客户端代码。

<强> 2。当我将生成的类型用作泛型类型参数时,为什么要调用ApplyStaticArguments?

请注意,我原来问题的重点是为什么ITypeProvider.ApplyStaticArguments被调用。调用ApplyStaticArguments的原因是因为GetStaticParameters说生成的类型(Vector2D)本身需要静态参数。修复GetStaticParameters后,不再为生成的类型调用ApplyStaticArguments。现在这是有道理的。

第3。如果ITypeProvider应该处理这种情况,那么正确的响应是什么?

上面已经说过了。然而,它提出了一个更大的问题,“生成型提供者的一个好例子在哪里?”已经提出了这个问题,但未回答here

最后,关于ProvideTypes.fs

在评论中,有一个问题是,使用ProvidedTypes.fs库是否可以解决此问题。我使用ProvidedTypes.fs重复了这个练习,最初遇到了同样的问题。也就是说,只要我在客户端代码中使用生成的类型作为泛型类型参数,客户端代码就不会编译。即使使用ProvidedTypes.fs,您也必须认识到ProvidedTypeDefinition.DefineStaticParameters(相当于ITypeProvider.GetStaticParameters)方法的处理程序必须考虑到它可能被称为传入生成类型的事实。< / p>