Поставщик типа F # - «возвращать только сгенерированные типы» - PullRequest
12 голосов
/ 18 сентября 2011

Попытка закодировать числа 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.Почему возвращаемый тип (поставщиком типа) должен соответствовать имени типа, которое я объявляю в потребителе?

Ответы [ 2 ]

15 голосов
/ 18 сентября 2011

Есть несколько важных вещей, которые нужно понять о поставщиках типов. Прежде всего, есть два вида предоставляемых типов:

  1. Генерируемые типы - это настоящие .NET-типы, которые внедряются в сборку, использующую провайдер типов (это те, которые провайдеры типов оборачивают в средствах генерации кода, например, в sqlmetal)
  2. Стираемые типы - это имитированные типы, которые представляются другим типом при компиляции кода.

Точно так же, как один на один, механизмы управления этим различием все еще находятся в воздухе. В предварительном просмотре необходимо использовать атрибут [<Generate>] в сборке, в которую встроены сгенерированные типы, и не следует использовать атрибут [<Generate>] при использовании удаленного предоставленного типа. Я полагаю (но не могу вспомнить наверняка), что на указанном конце сгенерированное значение определяется на основе свойства Assembly типа.

Кроме того, имейте в виду, что вы не обязательно хотите использовать фактические типы (например, через typeof<X>) при реализации API - вы часто будете хотеть использовать пользовательские типы, полученные из System.Type. Есть много инвариантов, которые должны быть удовлетворены среди различных методов. API поставщика необработанных типов не прост в использовании - я бы посоветовал дождаться выпуска некоторых примеров, в которых используется более приятная оболочка API (что, я надеюсь, должно произойти в течение следующих нескольких недель).

Сказав это, при быстром рассмотрении, по крайней мере, несколько вещей в вашем текущем подходе выглядят неправильно для меня:

  1. Тип, который вы возвращаете из ApplyStaticArguments, не имеет того же имени, что и аргумент typeNameWithArguments. Вероятно, именно поэтому вы получаете сообщение об ошибке при упоминании имен типов.
  2. Вы пытаетесь использовать сокращение типа, которое создает универсальный тип (например, WW<'a>) из неуниверсального типа (например, S<S<S<S<S<Z>>>>>).
4 голосов
/ 14 ноября 2011

Забыл обновить об этом: действительно, чего не хватало, так это «стираемого» флага типа (TypeProviderTypeAttributes.IsErased) в моем «экспортированном» типе.Я поставил свои эксперименты на github .

...