Попытка понять, как реализованы функторы в 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)

Строка означает:

  1. Определите функцию с именем ,
  2. С тремя кортежами параметров типов ^M, ^I, а также ^Rсоответственно,
  3. И убедитесь, что хотя бы один из этих типов имеет статический метод с именем Map,
  4. И этот метод принимает кортеж (^I * ^R) * ^M,
  5. И мы вызываем этот метод, передавая его ((source, mapping), mthd)как параметр.

Что касается того, почему он возвращает кортеж - это не так. Кортеж в последней строке не возвращается из функции, а передается функции в качестве параметра. Это результат callкоторый возвращается из Invoke.

Другие вопросы по тегам