可以扩展现有类型以使用Seq.sum等吗?

Jam*_*ard 7 f#

最近一直在与很多TimeSpans合作,并且需要获得总和和平均值.
但是,TimeSpan既不定义运算符get_Zero也不定义DivideByInt,因此Seq.sum和Seq.average不能直接使用此类型.以下无法编译:

open System
type System.TimeSpan
    with
        static member Zero with get() = TimeSpan()
        static member (/) (n:DateTime, d:int) = DateTime( n.Ticks / (int64) d )

let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
Run Code Online (Sandbox Code Playgroud)
  • 错误:"TimeSpan"类型不支持任何名为"get_Zero"的运算符
  • 错误:类型'TimeSpan'不支持任何名为'DivideByInt'的运算符
  • 警告:扩展成员无法提供操作员重载.考虑将运算符定义为类型定义的一部分.

是否有一些F#魔术可以在现有类型上定义这些运算符?

我知道以下内容可行(并且应该更有效地启动),但我仍然对上面的内容感到好奇,所以我可以将它添加到我的工具箱中以便与其他类型一起使用.

let sum = TimeSpan( ts |> Seq.sumBy (fun t -> t.Ticks) )
let avg = TimeSpan( let len = ts |> Seq.length in sum.Ticks / int64 len )
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 9

据我所知,静态成员约束(由函数使用Seq.sum)不能发现由类型扩展(实质上是扩展方法)添加的成员,所以我认为没有直接的方法来做到这一点.

我能想到的最好的选择是在System.TimeSpanstruct 周围创建一个简单的包装器.然后,您可以定义所有必需的成员.代码如下所示:

[<Struct>]
type TimeSpan(ts:System.TimeSpan) =
  member x.TimeSpan = ts
  new(ticks:int64) = TimeSpan(System.TimeSpan(ticks))
  static member Zero = TimeSpan(System.TimeSpan.Zero)
  static member (+) (a:TimeSpan, b:TimeSpan) = 
    TimeSpan(a.TimeSpan + b.TimeSpan)
  static member DivideByInt (n:TimeSpan, d:int) = 
    TimeSpan(n.TimeSpan.Ticks / (int64 d)) 

let ts = [ TimeSpan(10L); TimeSpan(99L) ] 
let sum = ts |> Seq.sum 
let avg = ts |> Seq.average 
Run Code Online (Sandbox Code Playgroud)

我调用了类型TimeSpan,因此它隐藏了标准System.TimeSpan类型.但是,ts.TimeSpan当您需要访问底层系统类型时,仍然需要编写,因此这不是很好.

  • @James:我相信`System.TimeSpan`中的`Zero`实际上是一个字段 - 而不是属性 - 而F#成员约束只能用于属性/方法.一个真实的例子表明使用字段会导致麻烦......! (2认同)