为什么这种内联用法会导致F#中的编译错误?

时间:2020-11-02 19:56:46

标签: f#

我正尝试了解有关inline和SRTP的更多信息。不幸的是,我没有使这个样本最小化的理解,但是它并不太大:

type ComponentCollection<'props, 'comp when 'props : comparison> =
  {
    Components : Map<'props, 'comp>
  }
  with
    static member AddComponent (collection, props, comp) =
      {
        Components =
          collection.Components
          |> Map.add props comp
      }

module ComponentCollection =

  let inline add< ^t, ^props, ^comp 
                 when ^t : (static member AddComponent : ((^t * ^props * ^comp) -> ^t)) > 
                 (props : ^props) (comp : ^comp) (xs : ^t) =
    (^t : (static member AddComponent : (^t * ^props * ^comp -> ^t ) ) () ) (xs, props, comp)

type Component<'t> =
  {
    Props : 't
  }

type Foo =
  {
    Foo : int
  }

[<EntryPoint>]
let main argv =

  let foo = { Foo = 1 }

  let a : ComponentCollection<Foo, Component<Foo>> =
    { Components = Map.empty }
    |> ComponentCollection.add foo { Props = foo }

  0

给出编译错误:

错误FS0001:类型'ComponentCollection <'a,'b>'不支持运算符'get_AddComponent'

由于我使用的是static member AddComponent,而不是get_AddComponent,所以这对我来说是非常误导的。我不确定编译器从何处获得get_AddComponent

我在这里做什么错了?

另外,如果有人可以解释这段代码,这将非常有帮助:

  (^t : (static member AddComponent : (^t * ^props * ^comp -> ^t ) ) () )

我知道它以某种方式提供了static的{​​{1}}成员AddComponent作为自由函数,但是我将其与其他示例结合在一起,并且缺少该语法的文档。例如,为什么需要^t

2 个答案:

答案 0 :(得分:2)

据我所知,您的代码片段唯一有问题的地方是您有一些多余的括号,不幸的是,在这种情况下,括号实际上具有语义含义。如下更改代码即可解决问题:

module ComponentCollection =
  let inline add< ^t, ^props, ^comp 
                 when ^t : (static member AddComponent : ^t * ^props * ^comp -> ^t) > 
                 (props : ^props) (comp : ^comp) (xs : ^t) =
    (^t : (static member AddComponent : ^t * ^props * ^comp -> ^t) (xs, props, comp))

问题在于F#实际上在以下方面有所作为:

static member AddComponent : ^t * ^props * ^comp -> ^t
static member AddComponent : (^t * ^props * ^comp) -> ^t
static member AddComponent : ((^t * ^props * ^comp) -> ^t)
  • 第一个是带有三个参数的常规静态方法
  • 第二个是将三元素元组作为单个参数的静态方法
  • 最后一个是静态属性,返回带元组的函数值

关于您的第二个问题-在工作版本中,呼叫如下:

(^t : (static member AddComponent : ^t * ^props * ^comp -> ^t) (xs, props, comp))

这告诉编译器访问静态成员(约束要求),并使用参数xspropscomp调用它。在您的原始版本中,这是在调用该属性的getter方法(但是令人困惑的是,它以某种方式允许您在不使用参数的情况下执行此操作-我怀疑以后会导致另一种类型的错误)。

答案 1 :(得分:2)

根据经验,SRTP非常复杂且难以使用。它们需要大量inline,并且错误消息往往非常晦涩。

我建议限制使用它们。 另外,如果您对此感兴趣,它们将不兼容C#。

我还更改了您的代码以解决问题:

type ComponentCollection<'props, 'comp when 'props : comparison> =
{
    Components : Map<'props, 'comp>
}
with
    member collection.AddComponent(props, comp) =
    {
        Components =
        collection.Components
        |> Map.add props comp
    }

module ComponentCollection =

let inline add< ^t, ^props, ^comp 
                when 'props : comparison and ^t : (member AddComponent : ^props * ^comp -> ^t) > 
                (props : ^props) (comp : ^comp) (xs : ^t) =
    (^t : (member AddComponent : ^props * ^comp -> ^t ) (xs, props, comp ))

type Component<'t> =
{
    Props : 't
}

type Foo =
{
    Foo : int
}

[<EntryPoint>]
let main argv =

    let foo = { Foo = 1 }

    let a  =
        { Components = Map.empty } 
        |> ComponentCollection.add foo { Props = foo }

    0
相关问题