Поставщик типа F# - "возвращать только сгенерированные типы"
Попытка кодировать числа 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
type - это просто пустышка, иначе я не смог бы пройти через провайдера типов. Код потребителя:
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
был возвращен. Почему возвращаемый тип (поставщиком типа) должен соответствовать имени типа, которое я объявляю в потребителе?
2 ответа
Есть несколько важных вещей, которые нужно понять о поставщиках типов. Прежде всего, есть два вида предоставляемых типов:
- Генерируемые типы - это настоящие типы.NET, которые внедряются в сборку, в которой используется поставщик типов (это те, которые используют поставщики типов, которые используют такие инструменты генерации кода, как sqlmetal)
- Стираемые типы - это симулированные типы, которые представляются другим типом при компиляции кода.
Механизмы контроля за этим различием все еще в воздухе. В предварительном просмотре вам нужно использовать [<Generate>]
атрибут в сборку, в которую встроены сгенерированные типы, и вы не должны использовать [<Generate>]
атрибут при использовании удаленного предоставленного типа. Я полагаю (но не могу вспомнить наверняка), что на предоставленном конце генерируется-ность определяется на основе типа Assembly
имущество.
Кроме того, имейте в виду, что вы не обязательно хотите использовать фактические типы (например, через typeof<X>
) при реализации API - вам часто нужно использовать пользовательские типы, полученные из System.Type
, Есть много инвариантов, которые должны быть удовлетворены среди различных методов. API поставщика необработанных типов не прост в использовании - я бы посоветовал дождаться выпуска некоторых примеров, использующих более приятную оболочку API (что, я надеюсь, должно произойти в течение следующих нескольких недель).
Сказав это, при быстром рассмотрении здесь по крайней мере несколько вещей в вашем текущем подходе, которые выглядят неправильно для меня:
- Тип, из которого вы возвращаетесь
ApplyStaticArguments
не имеет того же имени, что и аргументtypeNameWithArguments
, Вероятно, именно поэтому вы получаете сообщение об ошибке при упоминании имен типов. - Вы пытаетесь использовать сокращение типа, которое создает универсальный тип (например,
WW<'a>
) из неуниверсального типа (например,S<S<S<S<S<Z>>>>>
).
Забыл обновить по этому поводу: действительно, чего не хватало, так это "стираемого" флага типа (TypeProviderTypeAttributes.IsErased
) в моем "экспортированном" типе. Я поставил свои эксперименты на GitHub.