Could not extend operators in F#?

2019-02-19 00:22发布

module FSharp=
let Point2d (x,y)= Point2d(x,y)
let Point3d (x,y,z)= Point3d(x,y,z)
type NXOpen.Point3d with
    static member ( * ) (p:Point3d,t:float)= Point3d(p.X*t,p.Y*t,p.Z*t)
    static member ( * ) (t:float,p:Point3d)= Point3d(p.X*t,p.Y*t,p.Z*t)
    static member (+) (p:Point3d,t:float)= Point3d(p.X+t,p.Y+t,p.Z+t)
    static member (+) (t:float,p:Point3d)= Point3d(p.X+t,p.Y+t,p.Z+t)
    static member (+) (p:Point3d,t:Point3d)= Point3d(p.X+t.X,p.Y+t.Y,p.Z+t.Z)

let a=Point3d (1.,2.,3.)
let b=1.0
let c=a * b//error

Error 15: The type 'float' does not match the type
'Point3d' E:\Work\extension-RW\VS\extension\NXOpen.Extension.FSharp\Module1.fs 18 13 NXOpen.Extension.FSharp

I want to extend the Point3d methods, some new operators. But it doesn't pass over.

2条回答
劫难
2楼-- · 2019-02-19 00:39

Indeed it is possible. There is a way to extend binary operators using the one and only and little known ternary operator ?<-. So in your case you can try this:

type SumPoint3d = SumPoint3d with
    static member        (?<-) (p:Point3d, SumPoint3d, t        ) = Point3d(p.X + t  , p.Y + t  , p.Z + t  )
    static member        (?<-) (t        , SumPoint3d, p:Point3d) = Point3d(p.X + t  , p.Y + t  , p.Z + t  )
    static member        (?<-) (p:Point3d, SumPoint3d, t:Point3d) = Point3d(p.X + t.X, p.Y + t.Y, p.Z + t.Z)
    static member inline (?<-) (a        , SumPoint3d, b        ) = a + b

type ProdPoint3d = ProdPoint3d with    
    static member        (?<-) (p:Point3d, ProdPoint3d, t        ) = Point3d(p.X * t, p.Y * t, p.Z * t)
    static member        (?<-) (t        , ProdPoint3d, p:Point3d) = Point3d(p.X * t, p.Y * t, p.Z * t)
    static member inline (?<-) (a        , ProdPoint3d, b        ) = a * b

let inline ( + ) a b =  a ? (SumPoint3d ) <- b
let inline ( * ) a b =  a ? (ProdPoint3d) <- b

let a=Point3d (1.,2.,3.) 
let b=1.0

Now you can try:

> let c=a * b ;;
val c : Point3d = Point3d (1.0,2.0,3.0)

>  2 * 3 ;;
val it : int = 6
查看更多
Bombasti
3楼-- · 2019-02-19 00:47

If the Point3d type is declared in a separate assembly that you can't modify, then there is (unfortunately) no way to implement new overloads of the standard operators like + or *. The code in your question adds operators as extension methods, but the F# compiler doesn't search for extension methods when looking for overloaded operators.

If you can't modify the library, then there are three things you can do:

  • Create a wrapper for Point3d that stores a value of Point3d and implements all the operators
    (but this is likely going to be quite inefficient)

  • Define new operators that do not clash with the built-in ones. For example, you can use +$ and $+ for multiplication by scalar from the left and right. To declare such operator, you would write:

    let (+$) (f:float) (a:Point3d) = (...)
    
  • Implement your own Point3d type that does all the work, possibly with a conversion function that turns it into Point3d when you need to call a library.

It is hard to tell which option is the best - the second approach is probably the most efficient, but it will make code look a bit uglier. Depending on your scenario, the option 1 or 3 may work too.

查看更多
登录 后发表回答