F# 类型提供程序引用自定义类型

3
我正在构建一个简单的类型提供程序,但在引用我创建的类型时似乎遇到了问题。例如,给定以下代码:
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类型?

1个回答

6

这个错误通常意味着您正在创建一个包含自定义类型值的引用。类型提供程序中的引用只能包含原始类型的值-编译器知道如何序列化这些值,但它无法处理自定义类型。

在代码片段中,此问题出现在这里:

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

在这里,GetterCode 返回了一个引用类型为 Summation 的值,这种类型不被支持。要使其工作,你可以采取各种方法——通常情况下,你需要想出其他的引用表达式来产生所需的值。
其中一种选项是在引用内部进行计算而不是外部:
<@@ QuickAdd.add num1 num2 @@>

另一种选择是在报价单中重新创建"Summation"值:

Summation 值重新创建到报价单中。

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

这是可行的,因为它只需要序列化一个基本的int值,然后生成对Summation案例构造函数的调用。

1
太棒了,谢谢你的提示,我不知道引用方面的限制。除了Type Provider Starter Pack之外,你知道关于F#这个主题的好资源吗?相对于类型提供程序和引用,Expert F# v3似乎有些欠缺。 - Nick Babcock

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