F#自定义类型提供程序 - DefineStaticParameters实例化函数被多次调用

4

我有一个F#自定义类型提供程序(在这种情况下是CheckedRegexProvider);相关源码如下:

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

    let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)

    do regexTy.DefineStaticParameters(
        parameters=staticParams, 
        instantiationFunction=(fun typeName parameterValues ->

          match parameterValues with ...

然后我有一个简单的测试项目,在其中将一些琐碎的代码放在 .fs 文件中。

open Samples.FSharp.RegexTypeProvider
type T = RegexTyped< @"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)">

我感到困惑的是,实例化函数被调用的次数比我预期的要多得多。我以为只有在更改静态参数(在这种情况下为"(?^\d{3})-(?\d{3}-\d{4}$)")时才会调用该函数,但每次我在测试源中进行操作(例如按下空格键),该lambda函数都会连续调用两次。如果我真的更改了参数,它会被调用两次或三次。
当然,这对IDE(在我的情况下是Visual Studio)有相当大的影响,特别是因为该函数旨在提供一个类型,该类型可能需要扫描某些数据源以获取模式信息。
然后我尝试将类型提供程序调用隔离在一个单独的源模块文件中(我们称之为M1.fs),并在实际测试代码(M2.fs)中打开该模块。这样,每当我触摸M1.fs时,lambda函数仍会被调用,但在我工作于M2.fs时,它根本不会被调用,这是预期的。
我的问题是:这些连续的实例化函数重新调用是否正确?这是设计如此吗?
如果是,为什么会这样?
1个回答

4

类型提供程序在编译时被调用,编译器使用它们提供的类型来编译其余的代码。

几乎所有能够智能处理F#代码的IDE都会在幕后调用F#编译器服务来构建AST(通常是一次一个源文件),然后对该AST执行操作,例如提供Intellisense。

F#编译器服务不会缓存先前编译的结果,因为您所做的任何更改都可能会影响源文件中其他地方的代码,包括在它之前和之后。这个语句中“之后”的部分很明显,但可能不会立即明白为什么第25行的更改会影响第10行。原因在于类型推断:如果第10行是let a = Array.zeroCreate 1024,而第25行是a.[0] <- 42,那么a的类型将被推断为int[]。如果将第25行更改为a.[0] <- "forty-two",则a的类型将被推断为string[],并且为第10行构建的AST将不同。因此,F#编译器服务每次调用时都会重新编译整个源文件

因此,每次编辑源文件时,F#编译器服务都会重新编译该文件。如果该文件包含您的类型提供程序定义,则编译器必须实例化类型提供程序以便编译该文件,因此每次编译文件时都必须调用instantiationFunction

所以,是的,您看到的行为是有意设计的。


这就解释了为什么每次源更改后都会发生调用,但是:为什么要重复两次或三次呢?难道一个不够吗? - Franco Tiveron
我无法确定,因为我不知道IDE如何调用F#编译器服务的所有细节。但是猜测一下,IDE可能会调用编译器服务两次。我知道F#编译器服务提供了许多不同的服务,一个编译器可能想要使用它们,所以也许第一次调用是获取未经过类型检查的AST,而第二次调用是为了获取Intellisense所需的信息?实际答案可能可以在Visual Studio源代码中找到,但这需要更深入的挖掘,而我不愿意这样做。 - rmunn

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