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