Ответ 1
Это довольно интересно! У меня нет официальной информации (и я не видел этого документально нигде), но вот некоторые мысли о том, как функция 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
может повысить производительность, если вы вызываете функцию, переданную как аргумент, который принимает более одного аргумента много раз. Как и в случае с любыми оптимизациями, я не буду использовать это вслепую, но это интересно, когда нужно настраивать производительность!
(BTW: Спасибо за очень интересный вопрос, я не знал, что компилятор делает это: -))