试图了解函子在 FSharpPlus 中是如何实现的

Trying to understand how functors are implemented in FSharpPlus

谁能解释一下这段代码在 F# 中的工作原理:

https://github.com/fsprojects/FSharpPlus/blob/master/src/FSharpPlus/Control/Functor.fs#L99-99

static member inline Invoke (mapping: 'T->'U) (source: '``Functor<'T>``) : '``Functor<'U>`` = 
    let inline call (mthd: ^M, source: ^I, _output: ^R) = ((^M or ^I or ^R) : (static member Map : (_*_)*_ -> _) (source, mapping), mthd)
    call (Unchecked.defaultof<Map>, source, Unchecked.defaultof<'``Functor<'U>``>)

特别是 call 函数,它使用了我不理解的语法

例如

这种巫毒语法被称为 "Statically Resolved Type Parameters",或简称 SRTPs。

普通类型参数用勾号表示,如'a。这些类型参数由 .NET 运行时原生支持,因此具有此类类型参数的类型和函数将直接编译为 类 和 IL 中的方法。

SRTP 用抑扬符表示,如 ^M .NET 运行时支持,因此无法编译为 IL,因此具有在编译期间“展开”。这意味着对于这样参数化的函数的每个使用点,编译器必须确定 SRTP 应该是什么具体类型,并在调用点插入函数的整个主体,也称为“内联”。这就是 inline 关键字的用途。

SRTP 的要点是表达一个约束,说“具有方法的任何类型或属性 具有此签名”。在您的特定示例中,这是在这一行中表达的:

    let inline call (mthd: ^M, source: ^I, _output: ^R) = ((^M or ^I or ^R) : (static member Map : (_*_)*_ -> _) (source, mapping), mthd)
               ^^^^        ^^          ^^           ^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^
               (1)          \----------||-----------/                       (3)                        (4)                  (5)
                                       (2)

该行表示:

  1. 定义一个名为call
  2. 的函数
  3. 三个元组参数,类型分别为^M^I^R
  4. 并确保至少其中一种类型具有名为 Map
  5. 的静态方法
  6. 那个方法需要一个元组 (^I * ^R) * ^M,
  7. 我们调用该方法将其作为参数传递 ((source, mapping), mthd)

至于为什么它 returns 是一个元组 - 它不是。最后一行的元组不是从 Invoke 函数返回的,而是作为参数传递给 call 函数的。这是从 Invoke.

返回的 call 的结果