Попытка понять, как реализованы функторы в 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)
иметь в виду? - Какие части этого являются кодом, а какие — определениями типов?
- Как он имеет сигнатуру типа
... -> 'a
когда кажется, что он возвращает кортеж? - Происходит ли какое-то хакерское программирование на уровне типов?
1 ответ
Этот синтаксис вуду называется «Статически разрешенные параметры типа» или сокращенно SRTP.
Параметры нормального типа обозначаются галочкой, например
'a
. Эти параметры типа изначально поддерживаются средой выполнения .NET, поэтому типы и функции с такими параметрами типа компилируются непосредственно в классы и методы в IL.
SRTP обозначаются циркумфлексом, например , и не поддерживаются средой выполнения .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)
Строка означает:
- Определите функцию с именем ,
- С тремя кортежами параметров типов
^M
,^I
, а также^R
соответственно, - И убедитесь, что хотя бы один из этих типов имеет статический метод с именем
Map
, - И этот метод принимает кортеж
(^I * ^R) * ^M
, - И мы вызываем этот метод, передавая его
((source, mapping), mthd)
как параметр.
Что касается того, почему он возвращает кортеж - это не так. Кортеж в последней строке не возвращается из функции, а передается функции в качестве параметра. Это результат
call
который возвращается из
Invoke
.