Почему функция с byref не может быть преобразована напрямую в делегат? - PullRequest
6 голосов
/ 31 января 2012

В обычных условиях функции 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 Ответ

10 голосов
/ 31 января 2012

Мне кажется, проблема в том, что 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 # не делает этого и просит вас быть более явным ...

...