Оператор перегрузки в F#: (/)
Я хотел бы перегрузить оператор (/) в 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)
Есть идеи?
С уважением, форки
4 ответа
Вы не можете предоставить перегруженные операторы для существующих типов. Одним из вариантов является использование другого имени оператора (как предполагает 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
type, средство проверки типов не позволит вам совершить эту ошибку.
Более того, компилятор не позволит вам (просто) неправильно комбинировать пути (что может легко произойти, если вы представляете пути в виде строк), потому что p + p
не определено (вы можете использовать только /
, который правильно использует Path.Combine
).
Я не думаю, что есть прямой способ сделать это. Члены расширения не учитываются при перегрузке операторов в 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
На самом деле вы можете.
Попробуй это:
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
Я не думаю, что это возможно в F#, основываясь на чтении перегрузочной документации.
Вместо этого я бы предложил вам создать свою собственную функцию, которая выглядит как /
но нет. Что-то вроде:
let (</>) path1 path2 = Path.Combine (path1,path2)
Это, вероятно, будет менее раздражающим в конечном счете, потому что это не портит неявный вывод типа, который выполняет читатель-человек./
означает, что результатом является число с плавающей запятой, и помнить, что это иногда строка, является бременем *. Но после первого раза читатель видит </>
Легко запомнить, что он делает что-то, связанное с символом, встроенным в середину.
* Я думаю, что единственная причина +
для строк выглядит нормально, это передержка. После длительного использования Haskell или Caml первые несколько минут после переключения на другой язык "foo" + "bar"
выглядит ужасно плохо.