Это довольно интересно! У меня нет официальной информации (и я нигде этого не видел), но вот некоторые мысли о том, как может работать функция Adapt
.
Функции наподобие mapi
принимают каррированный вид функции, что означает, что тип аргумента компилируется во что-то вроде FSharpFunc<int, FSharpFunc<T, R>>
. Однако многие функции фактически скомпилированы непосредственно как функции двух аргументов, поэтому фактическое значение обычно будет FSharpFunc<int, T, R>
, которое наследуется от FSharpFunc<int, FSharpFunc<T, R>>
.
Если вы вызываете эту функцию (например, f 1 "a"
), компилятор F # генерирует что-то вроде этого:
FSharpFunc<int, string>.InvokeFast<a>(f, 1, "a");
Если вы посмотрите на функцию InvokeFast
с помощью Reflector, вы увидите, что она проверяет, скомпилирована ли функция как оптимизированная версия (f :? FSharpFunc<int, T, R>
). Если да, то он напрямую звонит Invoke(1, "a")
, а если нет, то ему нужно сделать два звонка Invoke(1).Invoke("a")
.
Эта проверка выполняется каждый раз, когда вы вызываете функцию, переданную в качестве аргумента (вероятно, быстрее выполнить проверку, а затем использовать оптимизированный вызов, потому что это более распространено).
Функция Adapt
выполняет преобразование любой функции в FSharpFunc<T1, T2, R>
(если функция не оптимизирована, она создает оболочку для нее, но в большинстве случаев это не так). Вызовы адаптированной функции будут выполняться быстрее, поскольку им не нужно каждый раз выполнять динамическую проверку (проверка выполняется только один раз внутри Adapt
).
Итак, в итоге, Adapt
может улучшить производительность, если вы вызываете функцию, переданную в качестве аргумента, который принимает более 1 аргумента большое количество раз. Как и в случае любых оптимизаций, я бы не стал использовать это вслепую, но это интересно знать при настройке производительности!
(Кстати: спасибо за очень интересный вопрос, я не знал, что компилятор делает это: -))