简体   繁体   中英

Overload operator in F#: (/)

I would like to overload the (/) operator in F# for strings and preserve the meaning for numbers.

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

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

If I try the following I get "Warning 29 Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead."

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

Any ideas?

Regards, forki

You cannot provide overloaded operators for existing types. One option is to use another operator name (as Natahan suggests). However, you can also define a new type to represent paths in your F# code and provide the / operator for this type:

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")

This has one important benefit - by making the types more explicit, you give the type checker more information that it can use to verify your code. If you use strings to represent paths, then you can easily confuse path with some other string (eg name). If you define your Path type, the type-checker will prevent you from making this mistake.

Moreover, the compiler won't allow you to (simply) combine paths incorrectly (which can easily happen if you represent paths as strings), because p + p is not defined (you can use only / , which correctly uses Path.Combine ).

Actually you can.

Try this:

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

I don't think that there is a straightforward way to do that. Extension members aren't taken into consideration for operator overloading in F#, and there isn't a good way to redefine the operation in a semi-generic way using member constraints.

It is possible to hack something together that will work, but it's very ugly:

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

I do not think this is possible in F#, based on a reading of the overloading documentation .

I would instead suggest that you create your own function which looks like / but isn't. Something like:

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

This is likely to be less annoying in the long run because it doesn't mess with the implicit type inference that the human reader is running-- / means that the result is a floating point, and remembering that it's sometimes a string is a burden*. But after the first time the reader sees </> , it's easy to remember that it does something related to the symbol embedded in the middle.

*I think the only reason + for strings looks OK is over-exposure. After using Haskell or Caml for a long time, the first few minutes after switching to another language makes "foo" + "bar" look jarringly bad.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM