F#中的重载运算符:(/)

for*_*i23 13 f# inline operator-overloading

我想重写F#中的(/)运算符以获取字符串并保留数字的含义.

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

let x = 3 / 4 // doesn't compile
Run Code Online (Sandbox Code Playgroud)

如果我尝试以下操作,我会得到"警告29扩展成员无法提供操作员重载.请考虑将操作符定义为类型定义的一部分."

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

有任何想法吗?

此致,forki

Tom*_*cek 20

您无法为现有类型提供重载运算符.一种选择是使用另一个运营商名称(如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")
Run Code Online (Sandbox Code Playgroud)

这有一个重要的好处 - 通过使类型更明确,您可以为类型检查器提供更多可用于验证代码的信息.如果使用字符串表示路径,则可以轻松地将路径与其他字符串(例如名称)混淆.如果您定义了Path类型,则类型检查器将阻止您犯这个错误.

此外,编译器不允许您(简单地)错误地组合路径(如果您将路径表示为字符串,这很容易发生),因为p + p未定义(您可以仅使用/,正确使用Path.Combine).

  • 当一个关于这个主题的书写一个人回答问题时,这很好! (2认同)

Gus*_*Gus 10

其实你可以.

试试这个:

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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这实际上与我的答案非常相似,虽然通过添加虚拟`DivExtension*DivExtesion - > DivExtension - > DivExtension`重载,您已经能够使用泛化第一个重载,同时防止编译器默认`(/)`操作员到第二次过载,这非常聪明.我觉得你使用`(=>)`符号运算符会使`(/)`的主体更难理解,尽管你可以省略显式的静态成员约束. (2认同)

kvb*_*kvb 9

我认为没有一种直接的方法可以做到这一点.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
Run Code Online (Sandbox Code Playgroud)


Nat*_*ers 6

基于对重载文档的阅读,我不认为这在F#中是可行的.

我建议你创建自己的功能,看起来像 /但不是.就像是:

let (</>) path1 path2 = Path.Combine (path1,path2)
Run Code Online (Sandbox Code Playgroud)

从长远来看,这可能不那么烦人,因为它不会弄乱人类读者正在运行的隐式类型推断 - /意味着结果是一个浮点,并且记住它有时是一个字符串是一个负担*.但是在读者第一次看到之后</>,很容易记住它与嵌入在中间的符号有关.

*我认为+字符串看起来不错的唯一原因是过度曝光.在使用Haskell或Caml很长一段时间后,切换到另一种语言后的前几分钟"foo" + "bar"看起来很糟糕.