Поставщик типов F#, ссылающийся на пользовательские типы

Я создаю простой поставщик типов, но, похоже, у меня возникают проблемы при обращении к созданным мной типам. Например, учитывая

namespace Adder

type Summation = Summation of int

module QuickAdd = 
  let add x y = x + y |> Summation

Я хочу пройти следующий тестовый пример:

module Adder.Tests

open Adder
open NUnit.Framework

type Simple = QuickAddProvider<1, 2>

[<Test>]
let ``Simple sample is 3`` () =
  let foo = Simple()
  Assert.AreEqual(foo.Sample, Summation 3)

С поставщиком следующего типа:

namespace Adder

open Microsoft.FSharp.Core.CompilerServices
open ProviderImplementation.ProvidedTypes
open System.Reflection

[<TypeProvider>]
type public QuickAddProvider (config : TypeProviderConfig) as this =
  inherit TypeProviderForNamespaces ()

  let ns = "Adder"
  let asm = Assembly.GetExecutingAssembly()
  let paraProvTy = ProvidedTypeDefinition(asm, ns, "QuickAddProvider", Some typeof<obj>)

  let buildTypes (typeName:string) (args:obj[]) =
    let num1 = args.[0] :?> int
    let num2 = args.[1] :?> int
    let tpType = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<obj>)
    let result = QuickAdd.add num1 num2
    let orig = ProvidedProperty("Sample", typeof<Summation>, GetterCode = (fun args -> <@@ result @@>))
    tpType.AddMember(orig)
    tpType.AddMember(ProvidedConstructor([], InvokeCode = (fun args -> <@@ () @@>)))
    tpType

  let parameters = 
    [ProvidedStaticParameter("Num1", typeof<int>)
     ProvidedStaticParameter("Num2", typeof<int>)]

  do paraProvTy.DefineStaticParameters(parameters, buildTypes)
  do this.AddNamespace(ns, [paraProvTy])

[<TypeProviderAssembly>]
do()

Я сталкиваюсь с неожиданными ошибками в тестовом файле:

The type provider 'Adder.QuickAddProvider' reported an error in the context of provided type 'Adder.QuickAddProvider,Num1="1",Num2="2"', member 'get_Sample'. The error: Unsupported constant type 'Adder.Summation'

Со следующими ошибками в сгенерированном файле:

The type "Summation" is not defined
The namespace or module "Adder" is not defined

Тестовый пример компилируется и проходит при замене типа Summation на int, поэтому я знаю, что мой поставщик типов не так уж неправ. Нужно ли мне как-то «импортировать» куда-то тип Summation?


person Nick Babcock    schedule 08.04.2015    source источник


Ответы (1)


Эта ошибка обычно означает, что вы создаете предложение, содержащее значение пользовательского типа. Кавычки в поставщиках типов могут содержать только значения примитивных типов — компилятор знает, как их сериализовать, — но он не может обрабатывать пользовательские типы.

Во фрагменте это происходит здесь:

let result = QuickAdd.add num1 num2
let orig = ProvidedProperty("Sample", typeof<Summation>, GetterCode = (fun args -> 
  <@@ result @@>))

Здесь GetterCode возвращает цитату, содержащую значение типа Summation, которое не поддерживается. Чтобы это работало, вы можете делать разные вещи — как правило, вам нужно придумать какое-то другое выражение в кавычках, которое дает желаемое значение.

Один из вариантов — выполнить расчет внутри цитаты, а не снаружи:

<@@ QuickAdd.add num1 num2 @@>

Другой вариант — воссоздать значение Summation в цитате:

let (Summation n) = result
<@@ Summation n @@>

Это работает, потому что нужно только сериализовать примитивное значение int, а затем сгенерировать вызов конструктора case Summation.

person Tomas Petricek    schedule 08.04.2015
comment
Круто, спасибо за подсказку, я не знал об этом ограничении цитат. Знаете ли вы какие-либо полезные ресурсы по этой теме F#, помимо начального пакета Type Provider? В Expert F# v3 немного не хватает поставщиков типов и цитат. - person Nick Babcock; 08.04.2015