Оператор перегрузки в F #: (/) - PullRequest
12 голосов
/ 11 мая 2010

Я бы хотел перегрузить оператор (/) в F # для строк и сохранить значение для чисел.

/// Combines to path strings
let (/) path1 path2 = Path.Combine(path1,path2)

let x = 3 / 4 // doesn't compile

Если я попытаюсь сделать следующее, я получу «Предупреждение 29 Элементы расширения не могут обеспечить перегрузки операторов. Попробуйте вместо этого определить оператор как часть определения типа».

/// Combines to path strings
type System.String with
  static member (/) (path1,path2) = Path.Combine(path1,path2)

Есть идеи?

С уважением, forki

Ответы [ 4 ]

19 голосов
/ 11 мая 2010

Вы не можете предоставить перегруженные операторы для существующих типов. Одним из вариантов является использование другого имени оператора (как предполагает Natahan). Однако вы также можете определить новый тип для представления путей в вашем коде F # и предоставить оператор / для этого типа:

open System    

// Simple type for representing paths
type Path(p) =
  // Returns the path as a string
  member x.Path = p 
  // Combines two paths
  static member ( / )(p1:Path, p2:Path) = 
    Path(IO.Path.Combine(p1.Path, p2.Path))

let n = 4 / 2
let p = Path("C:\\") / Path("Temp")

Это имеет одно важное преимущество - делая типы более явными, вы предоставляете контролеру типов больше информации, которую он может использовать для проверки вашего кода. Если вы используете строки для представления путей, вы можете легко спутать путь с какой-либо другой строкой (например, именем). Если вы определите свой тип Path, средство проверки типов предотвратит вашу ошибку.

Более того, компилятор не позволит вам (просто) неправильно комбинировать пути (что может легко случиться, если вы представляете пути в виде строк), поскольку p + p не определено (вы можете использовать только /, что правильно использует Path.Combine).

9 голосов
/ 13 декабря 2011

На самом деле вы можете.

Попробуйте это:

open System.IO

type DivExtension = DivExtension with
    static member inline (=>) (x             , DivExtension) = fun y -> x / y
    static member        (=>) (x             , DivExtension) = fun y -> Path.Combine(x, y)
    static member        (=>) (x:DivExtension, DivExtension) = fun DivExtension -> x

let inline (/) x y = (x => DivExtension) y
8 голосов
/ 11 мая 2010

Я не думаю, что есть прямой способ сделать это. Элементы расширения не учитываются при перегрузке операторов в F #, и нет хорошего способа переопределить операцию полуобобщенным способом с использованием ограничений элементов.

Можно взломать что-то вместе, что сработает, но это очень уродливо:

type DivisionOperations =
  static member Divide(x:int, y:int) = x / y
  static member Divide(path1, path2) = Path.Combine(path1, path2)

let inline div< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Divide : ^a * ^b -> ^c)> a b = ((^t or ^a) : (static member Divide : ^a * ^b -> ^c) (a, b))

let inline (/) x y = div<DivisionOperations, _, _, _> x y
6 голосов
/ 11 мая 2010

Я не думаю, что это возможно в F #, основываясь на чтении документации по перегрузке .

Вместо этого я бы предложил вам создать собственную функцию, которая выглядит как /, но это не так. Что-то вроде:

let (</>) path1 path2 = Path.Combine (path1,path2)

Это, вероятно, будет менее раздражающим в долгосрочной перспективе, потому что это не портит неявный вывод типа, который выполняет читатель-человек - / означает, что результат является плавающей точкой, и помня, что иногда строка - это бремя *. Но после того, как читатель впервые видит </>, легко вспомнить, что он делает что-то, связанное с символом, встроенным в середину.

* Я думаю, единственная причина, по которой + выглядит так, что строки выглядят нормально, это чрезмерная экспозиция. После длительного использования Haskell или Caml первые несколько минут после переключения на другой язык "foo" + "bar" выглядят крайне плохо.

...