Почему функция с byref не может быть преобразована напрямую в делегат?
В обычных условиях функции F# могут быть преобразованы в делегаты путем вызова new DelegateType
и передача функции в качестве аргумента. Но когда делегат содержит byref
Параметр, это невозможно напрямую. Например код:
type ActionByRef<'a> = delegate of 'a byref -> unit
let f (x:double byref) =
x <- 6.0
let x = ref 42.0
let d = new ActionByRef<_>(f)
не скомпилируется, выдав следующую ошибку:
Это значение функции используется для создания типа делегата, чья сигнатура включает аргумент byref. Вы должны использовать явное лямбда-выражение, принимающее 1 аргумент.
После ошибки измените код для использования
let d = new ActionByRef<_>(fun x -> f(&x))
работает. Но мой вопрос: зачем это нужно? Почему F# не разрешает преобразование из именованной функции в этот делегат, но преобразование из лямбды в порядке?
Я столкнулся с таким поведением при исследовании другого вопроса. я понимаю byref
предназначен только для совместимости с другими языками.Net.
1 ответ
Я думаю, что проблема в том, что byref<'T>
не является фактическим типом в F# - он выглядит как тип (чтобы упростить язык), но он компилируется в параметр, помеченный out
флаг. Это означает, что byref<'T>
может использоваться только в месте, где компилятор может использовать out
флаг.
Проблема со значениями функций заключается в том, что вы можете создать функцию, например, путем частичного применения:
let foo (n:int) (b:byref<int>) =
b <- n
Когда вы проходите foo
в качестве аргумента конструктора делегата это частный случай частичного применения (без аргументов), но частичное приложение фактически должно создать новый метод, а затем передать его делегату:
type IntRefAction = delegate of byref<int> -> unit
let ac = IntRefAction(foo 5)
Компилятор может быть умным и генерировать новый метод с byref
параметр (или out
флаг), а затем передать его по ссылке на фактическую функцию, но, как правило, будет другой метод, сгенерированный компилятором, когда вы не используете fun ... -> ...
синтаксис. Обработка этого добавила бы сложности, и я думаю, что это относительно редкий случай, поэтому компилятор F# не делает этого и просит вас быть более явным...