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

Sha*_*ane 2 f# f#+

有人可以解释一下这段代码在 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>``>)
Run Code Online (Sandbox Code Playgroud)

具体来说call,该函数使用了我不理解的语法

例如

  • 这是什么(^M or ^I or ^R)意思?
  • 其中哪些部分是代码,哪些部分是类型定义?
  • ... -> 'a当它似乎返回一个元组时,它如何具有类型签名?
  • 是否存在某种类型级别的编程黑客行为?

Fyo*_*kin 5

这种巫毒语法称为“静态解析类型参数”,简称 SRTP。

普通类型参数用勾号表示,例如'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)
Run Code Online (Sandbox Code Playgroud)

该行的意思是:

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

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