F#类型提供程序 - “仅返回生成的类型”

12
尝试使用类型提供程序对类型级Peano数字进行编码:
namespace TypeProviderPlayground

open System
open Microsoft.FSharp.Core.CompilerServices
open System.Runtime.CompilerServices

[<assembly: TypeProviderAssembly()>]
do()

type Z = class end
type 'a S = class end
type N = class end

[<TypeProvider>]
type PeanoProvider(s: TypeProviderConfig) =
    let invalidate = Event<_,_>()
    interface ITypeProvider with
        member x.ApplyStaticArguments(typeWithoutArguments, typeNameWithArguments, staticArguments) =
            let n : int = unbox staticArguments.[0]
            [1..n] |> List.fold (fun s _ -> typedefof<S<_>>.MakeGenericType [| s |]) typeof<Z>
        member x.GetNamespaces() = 
            let ns = 
                { new IProvidedNamespace with
                    member x.GetNestedNamespaces() = [||]
                    member x.GetTypes() = [||]
                    member x.ResolveTypeName t =
                        if t = "N"
                            then typeof<N>
                            else null
                    member x.NamespaceName = "Peano" }
            [| ns |]
        member x.GetStaticParameters t =
            let p = 
                { new Reflection.ParameterInfo() with
                    member z.Name = "number"
                    member z.ParameterType = typeof<int> }
            [| p |]

        [<CLIEvent>]
        member x.Invalidate = invalidate.Publish
        member x.Dispose() = ()
        member x.GetInvokerExpression(syntheticMethodBase, parameters) = 
            raise <| NotImplementedException()
N类型只是一个虚拟的类型,否则我就无法通过类型提供程序。 使用者代码:
open TypeProviderPlayground

[<Generate>]
type S<'a> = Peano.N<5>

我收到了这个错误信息:

error FS3152: The provider 'TypeProviderPlayground.PeanoProvider' returned a non-generated type
'TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.Z, TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]' 
in the context of a [<Generate>] declaration. Either remove the [<Generate>] declaration or adjust the type provider to only return generated types. 

这段代码表明类型已经正确构造(Z S S S S S),但是由于某些原因,编译器不会将其接受为“生成的类型”。

如果我删除[<Generated>]属性,则会出现其他错误提示要求我添加该属性。

这是否意味着类型提供程序仅适用于动态发出的类型(这一要求乍一看似乎很奇怪)?

另外,如果我执行以下操作:

[<Generate>]
type WW<'a> = Peano.N<5>

我得到一个错误,说期望的是 WW'1,但返回的却是 S'1。为什么类型提供者返回的类型必须与消费者声明的类型名称匹配?

据我记得,从唐在//build上的演示中可以看出,他使用的自定义类型提供程序的示例是继承自一个类型,而不是实现接口。这可能更容易使用,例如它可能为那些不清楚的接口部分提供可重用的代码(如GetInvokerExpression)。 - Joh
如果你在视频的50:49处看一下,你会发现SampleTypeProvider继承自TypeProviderForNamespaces。这个类还没有发布。如果你等不及了,也可以参考http://strangelights.com/blog/archive/2011/09/18/first-example-of-a-very-simple-type-provider.aspx,看看Robert是如何填补一些空缺的。 - Joh
@Joh:我认为GetInvokerExpression不适用于我的示例。 - Mauricio Scheffer
1
可能会对您感兴趣:http://blogs.msdn.com/b/fsharpteam/archive/2011/09/24/developing-f-type-providers-with-the-f-3-0-developer-preview-an-introductory-guide-and-samples.aspx - kvb
2个回答

15

关于类型提供程序,有一些重要的事情需要了解。首先,提供的类型分为两种:

  1. 生成的类型 是真正嵌入到使用类型提供程序的程序集中的.NET类型(这是像sqlmetal这样的代码生成工具包装的类型提供程序所使用的)
  2. 擦除的类型 是模拟类型,在编译代码时由其他类型表示。

需要注意的是,控制这种区别的机制仍然存在一些问题。在预览版中,您需要在嵌入生成的类型的程序集中使用 [<Generate>] 属性,在使用被擦除的提供的类型时,不应该使用 [<Generate>] 属性。我相信(但现在记不太清楚了),在提供端,已生成的类型是基于类型的 Assembly 属性来确定的。

此外,请记住,在实现API时,您不一定想使用实际类型(例如通过 typeof<X>),您经常需要使用从 System.Type 派生的自定义类型。不同方法之间必须满足许多不变量。原始类型提供程序API并不容易使用-我建议等待发布一些使用更好的API封装器的示例(我希望应该会在接下来的几周内进行)。

话虽如此,经过快速查看,您当前的方法中至少有一些看起来不正确的地方:

  1. ApplyStaticArguments 返回的类型与参数 typeNameWithArguments 的名称不同。可能这就是为什么您会收到提到类型名称的错误的原因。
  • 您正在尝试使用类型缩写,从非泛型类型(例如S<S<S<S<S<Z>>>>>)创建泛型类型(例如WW<'a>)。

  • 谢谢!+1 关于第一个评论,为什么生成的类型名称必须与使用者的类型名称匹配?对于类型生成器来说,必须知道使用者的类型名称似乎相当奇怪。关于您的第二个建议,我确实尝试过绑定到非泛型类型,但是我得到了有关不匹配的类型名称(S vs S'1)的相同错误。 - Mauricio Scheffer
    @Mauricio - 生成类型的用途与普通类型缩写非常不同,因为它们定义了真正的.NET类型应该嵌入到哪里。因此,如果您有一个类似[<Generate>]type T = ...的定义,生成的程序集将实际包含一个类型T,并且类型提供程序基础结构期望提供程序相应地生成这样的类型。 - kvb
    谢谢,但它仍然不起作用。我尝试做的事情是否可能实现,还是我应该放弃? - Mauricio Scheffer
    @Mauricio - 嗯,如果您能详细说明您要做什么,那将会很有帮助... 您期望类型提供程序的用户编写什么代码,并且您希望它编译成什么? - kvb
    1
    @Mauricio - 在这种情况下,您需要一个擦除类型提供程序,但是如果不使用更友好的包装器覆盖原始API,则很难创建它。 - kvb
    显示剩余2条评论

    4

    忘记更新了:事实上,我“导出”的类型缺少“已擦除”类型标志(TypeProviderTypeAttributes.IsErased)。我把我的实验放到了github上


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接