Не могу перегрузить логические операторы в F # - PullRequest
2 голосов
/ 15 февраля 2011

F # допускает перегрузку арифметических операторов, таких как +, но, по-видимому, запрещает это для логических операторов, таких как ||. Следующий код генерирует предупреждение и две ошибки:

type MyBool =
    val Value : bool
    new(value) = { Value = value }
    static member (||) (v1: MyBool, v2 : MyBool) =
        new MyBool(v1.Value || v2.Value)
let b1 = new MyBool(true)
let b2 = new MyBool(false)
let b3 = b1 || b2

Предупреждение (в определении статического члена (||)): имя '(||)' не должно использоваться в качестве имени члена. Если вы определяете статический член для использования в других языках CLI, используйте вместо этого имя op_BooleanOr.

Ошибка (для b1 и b2 в операторе 'let b3'): ожидалось, что это выражение будет иметь тип bool, но здесь имеет тип MyBool

Если вместо (||) использовать op_BooleanOr, предупреждение исчезнет, ​​но ошибки останутся.

Когда я делаю то же самое для оператора + в типе MyInt, предупреждений или ошибок нет. Итак, почему эти предупреждения / ошибки появляются, когда я пытаюсь перегрузить || или &&?

Ответы [ 2 ]

6 голосов
/ 15 февраля 2011

Боюсь, что в компиляторе F # нет обработки логических операторов, которые позволили бы вам их переопределить (как в C #).Насколько я могу судить, x && y компилируется просто как if x then y else false, поэтому x должно быть логическим.Я не проверял, поддерживает ли компилятор F # это поведение для типов, объявленных в C #, но я не думаю, что это так.

Насколько я знаю, лучший способ эмулировать короткое замыкание behvaior для вашегособственный оператор должен использовать ключевое слово lazy для создания отложенных значений.Затем вы можете написать что-то вроде:

let foo b = 
  printfn "foo %b" b
  MyBool(b)

lazy foo true &&! lazy foo false    // Calls 'foo' for both branches
lazy foo false &&! lazy foo false   // Calls 'foo' only for the first one

Эти два оператора могут быть определены с использованием статических ограничений членов, поэтому они должны (в принципе) работать для любых типов, которые реализуют операторы, требуемые C #.

let inline (&&!) (x:Lazy<_>) (y:Lazy<_>) = 
  if (^T: (static member op_False : ^T -> bool) x.Value)
    then x.Value else x.Value &&& y.Value

let inline (||!) (x:Lazy<_>) (y:Lazy<_>) = 
  if (^T: (static member op_False : ^T -> bool) x.Value) 
    then x.Value else x.Value ||| y.Value

Затем вы можете определить свои типы MyBool со всеми необходимыми операторами (в качестве примечания: он должен использоваться естественным образом из C #, если вы определите его следующим образом):

type MyBool(b) =
  member x.Value = b
  static member (|||) (v1: MyBool, v2 : MyBool) = 
    MyBool(v1.Value || v2.Value) 
  static member (&&&) (v1: MyBool, v2 : MyBool) = 
    MyBool(v1.Value && v2.Value) 
  static member op_True (v: MyBool) = v.Value
  static member op_False (v: MyBool) = not v.Value
4 голосов
/ 15 февраля 2011

&& и || отличаются от других операторов тем, что они имеют короткое замыкание и поэтому не могут быть реализованы просто как методы.Из-за этого .net определяет специальные правила , которым вы должны следовать, чтобы разрешить использование && и || для ваших собственных типов.* и operator false.

...