试图了解函子在 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
函数,它使用了我不理解的语法
例如
(^M or ^I or ^R)
是什么意思?
- 其中哪些部分是代码,哪些部分是类型定义?
- 当它看起来 return 是一个元组时,它如何具有类型签名
... -> 'a
?
- 是否正在进行某种类型级别的编程黑客活动?
这种巫毒语法被称为 "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)
该行表示:
- 定义一个名为
call
、 的函数
- 三个元组参数,类型分别为
^M
、^I
、^R
,
- 并确保至少其中一种类型具有名为
Map
、 的静态方法
- 那个方法需要一个元组
(^I * ^R) * ^M
,
- 我们调用该方法将其作为参数传递
((source, mapping), mthd)
。
至于为什么它 returns 是一个元组 - 它不是。最后一行的元组不是从 Invoke
函数返回的,而是作为参数传递给 call
函数的。这是从 Invoke
.
返回的 call
的结果
谁能解释一下这段代码在 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
函数,它使用了我不理解的语法
例如
(^M or ^I or ^R)
是什么意思?- 其中哪些部分是代码,哪些部分是类型定义?
- 当它看起来 return 是一个元组时,它如何具有类型签名
... -> 'a
? - 是否正在进行某种类型级别的编程黑客活动?
这种巫毒语法被称为 "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)
该行表示:
- 定义一个名为
call
、 的函数
- 三个元组参数,类型分别为
^M
、^I
、^R
, - 并确保至少其中一种类型具有名为
Map
、 的静态方法
- 那个方法需要一个元组
(^I * ^R) * ^M
, - 我们调用该方法将其作为参数传递
((source, mapping), mthd)
。
至于为什么它 returns 是一个元组 - 它不是。最后一行的元组不是从 Invoke
函数返回的,而是作为参数传递给 call
函数的。这是从 Invoke
.
call
的结果