f#类型提供程序-“只返回生成的类型”

时间:2022-02-17 17:04:01

Trying to encode type-level peano numbers using a type provider:

尝试使用类型提供程序对类型级别的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()

The N type is just a dummy, otherwise I couldn't get it to go through the type provider. Consumer code:

N类型只是一个虚类型,否则我无法让它通过类型提供程序。消费者代码:

open TypeProviderPlayground

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

And I get this error:

我得到了这个错误

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. 

Which says that the type was correctly constructed (Z S S S S S) but for some reason the compiler won't accept it as a "generated type".

它说类型是正确构造的(Z S S S S S S),但是出于某种原因,编译器不会接受它为“生成的类型”。

If I remove the [<Generated>] attribute I get some other error telling me to add it.

如果我删除[< generate >]属性,我将得到一些其他错误,告诉我添加它。

Does this mean that type providers will only work on dynamically emitted types (which seems a weird requirement at first blush)?

这是否意味着类型提供者只处理动态发出的类型(乍一看,这似乎是一个奇怪的要求)?

Also, if I do:

同样,如果我做的事:

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

I get an error saying that WW'1 was expected but S'1 was returned. Why does the returned type (by the type provider) have to match the type name I declare in the consumer?

我得到一个错误消息说WW'1是预期的,而S'1是返回的。为什么返回的类型(由类型提供程序)必须匹配我在使用者中声明的类型名称?

2 个解决方案

#1


15  

There are a few important things to realize about type providers. First of all, there are two kinds of provided types:

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

  1. Generated types are real .NET types that get embedded into the assembly that uses the type provider (this is what the type providers that wrap code generation tools like sqlmetal use)
  2. 生成的类型是嵌入到使用类型提供程序的程序集中的真正的。net类型(这是包装代码生成工具的类型提供程序,如sqlmetal使用的)
  3. Erased types are simulated types which are represented by some other type when the code is compiled.
  4. 擦除类型是在编译代码时由其他类型表示的模拟类型。

Just as a heads-up, the mechanisms for controlling this distinction are still somewhat up in the air. In the preview, you need to use the [<Generate>] attribute in the assembly into which generated types are being embedded, and you should not use the [<Generate>] attribute when using an erased provided type. I believe (but can't remember for sure off hand) that on the provided end generated-ness is determined based on the type's Assembly property.

就像一个警告,控制这一区别的机制仍然有些悬而未决。在预览中,您需要在程序集中使用[ ]属性,并将生成的类型嵌入其中,并且在使用被删除的提供类型时不应该使用[ ]属性。我相信(但我不能马上确定)在提供的端生成度是基于类型的汇编属性确定的。

Also, keep in mind that you don't necessarily want to use actual types (e.g. via typeof<X>) when implementing the API - you'll frequently want to use custom types derived from System.Type. There are a lot of invariants that must be satisfied among the different methods. The raw type provider API is not easy to use - I'd suggest waiting for some examples to be released which use a nicer API wrapper (which I hope should take place within the next few weeks).

另外,请记住,在实现API时,您并不一定希望使用实际的类型(例如,通过类型 )——您通常希望使用从System.Type派生的自定义类型。在不同的方法之间必须满足许多不变量。原始类型提供者API不容易使用——我建议等待一些使用更好的API包装器的示例发布(我希望在未来几周内发布)。

Having said that, from a quick look here are at least a few things in your current approach which look wrong to me:

话虽如此,简单地看一下,你目前的方法中至少有一些我认为不对的地方:

  1. The type that you're returning from ApplyStaticArguments doesn't have the same name as the argument typeNameWithArguments. Presumably this is why you're getting the error mentioning the type names.
  2. 从ApplyStaticArguments返回的类型与参数typeNameWithArguments没有相同的名称。这大概就是为什么您会得到提到类型名的错误。
  3. You're trying to use a type abbreviation which creates a generic type (e.g. WW<'a>) from a non-generic type (e.g. S<S<S<S<S<Z>>>>>).
  4. 您正在尝试使用一个类型缩写,它创建了一个非泛型类型的泛型类型(例如,WW<'a>)(例如,S >>>)。

#2


4  

Forgot to update on this: indeed what was missing was the 'erased' type flag (TypeProviderTypeAttributes.IsErased) in my 'exported' type. I put my experiments on github.

忘记对此进行更新了:实际上所缺少的是我的“导出”类型中的“擦除”类型标志(typeprovidertypeattributes.is擦除)。我把实验放在github上。

#1


15  

There are a few important things to realize about type providers. First of all, there are two kinds of provided types:

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

  1. Generated types are real .NET types that get embedded into the assembly that uses the type provider (this is what the type providers that wrap code generation tools like sqlmetal use)
  2. 生成的类型是嵌入到使用类型提供程序的程序集中的真正的。net类型(这是包装代码生成工具的类型提供程序,如sqlmetal使用的)
  3. Erased types are simulated types which are represented by some other type when the code is compiled.
  4. 擦除类型是在编译代码时由其他类型表示的模拟类型。

Just as a heads-up, the mechanisms for controlling this distinction are still somewhat up in the air. In the preview, you need to use the [<Generate>] attribute in the assembly into which generated types are being embedded, and you should not use the [<Generate>] attribute when using an erased provided type. I believe (but can't remember for sure off hand) that on the provided end generated-ness is determined based on the type's Assembly property.

就像一个警告,控制这一区别的机制仍然有些悬而未决。在预览中,您需要在程序集中使用[ ]属性,并将生成的类型嵌入其中,并且在使用被删除的提供类型时不应该使用[ ]属性。我相信(但我不能马上确定)在提供的端生成度是基于类型的汇编属性确定的。

Also, keep in mind that you don't necessarily want to use actual types (e.g. via typeof<X>) when implementing the API - you'll frequently want to use custom types derived from System.Type. There are a lot of invariants that must be satisfied among the different methods. The raw type provider API is not easy to use - I'd suggest waiting for some examples to be released which use a nicer API wrapper (which I hope should take place within the next few weeks).

另外,请记住,在实现API时,您并不一定希望使用实际的类型(例如,通过类型 )——您通常希望使用从System.Type派生的自定义类型。在不同的方法之间必须满足许多不变量。原始类型提供者API不容易使用——我建议等待一些使用更好的API包装器的示例发布(我希望在未来几周内发布)。

Having said that, from a quick look here are at least a few things in your current approach which look wrong to me:

话虽如此,简单地看一下,你目前的方法中至少有一些我认为不对的地方:

  1. The type that you're returning from ApplyStaticArguments doesn't have the same name as the argument typeNameWithArguments. Presumably this is why you're getting the error mentioning the type names.
  2. 从ApplyStaticArguments返回的类型与参数typeNameWithArguments没有相同的名称。这大概就是为什么您会得到提到类型名的错误。
  3. You're trying to use a type abbreviation which creates a generic type (e.g. WW<'a>) from a non-generic type (e.g. S<S<S<S<S<Z>>>>>).
  4. 您正在尝试使用一个类型缩写,它创建了一个非泛型类型的泛型类型(例如,WW<'a>)(例如,S >>>)。

#2


4  

Forgot to update on this: indeed what was missing was the 'erased' type flag (TypeProviderTypeAttributes.IsErased) in my 'exported' type. I put my experiments on github.

忘记对此进行更新了:实际上所缺少的是我的“导出”类型中的“擦除”类型标志(typeprovidertypeattributes.is擦除)。我把实验放在github上。