Pavel Voronin
Pavel Voronin

Reputation: 13985

Is it possible to require statically resolved type parameter to be a generic type?

Having a hierarchy

open FSharp.Data.UnitSystems.SI.UnitSymbols

type Vector2D<[<Measure>] 'u>(x: float<'u>, y: float<'u>) =
   member val Abs = 
     let squared = float (x*x+y*y)
     LanguagePrimitives.FloatWithMeasure<'u> (Math.Sqrt squared)      

type R2D = 
    inherit Vector2D<m>(x, y)
    member val X = x
    member val Y = y

type V2D(vx, vy) = 
    inherit Vector2D<m/s>(vx, vy)
    member val Vx = vx
    member val Vy = vy       

type A2D(ax, ay) =
    inherit Vector2D<m/s^2>(ax, ay)
    member val Ax = ax
    member val Ay = ay

I would like to define global inline operator (+) (l:^v) (r:^v) where ^v would be required to inherit from Vector2D, have some unit of measure float<'u> and have constructor with two parameters of type float<'u>.

Is it possible?

Upvotes: 2

Views: 1088

Answers (2)

Assassin
Assassin

Reputation: 1302

You can try to do define the add function and then assign it to infix operator:

let add<[<Measure>]'u, 'v when 'v :> Vector2D<'u>> (l:^v) (r:^v) = l.Abs + r.Abs

let (+) left right = add left right

EDITED: This is how you can use it: IN this case left and right types has to match

let left = R2D(1.0<m>,1.0<m>)
let right = R2D(2.0<m>,2.0<m>)
let result = left + right

or you can try without matching types, still unit has to match

let left:Vector2D<m> = R2D(1.0<m>,1.0<m>) :> Vector2D<m>
let right:Vector2D<m> = A2D2(2.0<m>,2.0<m>) :> Vector2D<m>
let result = left + right

Upvotes: 4

LSM07
LSM07

Reputation: 817

You can define a separate + for each subtype of Vector2D like so:

type V2D(vx, vy) = 
    inherit Vector2D<m/s>(vx, vy)
    member val Vx = vx
    member val Vy = vy
    static member (+) (l:V2D, r:V2D) = V2D(l.Vx + r.Vx, l.Vy + r.Vy)

type A2D(ax, ay) =
    inherit Vector2D<m/s^2>(ax, ay)
    member val Ax = ax
    member val Ay = ay
    static member (+) (l:A2D, r:A2D) = A2D(l.Ax + r.Ax, l.Ay + r.Ay)

let v1 = V2D(2.0<m/s>, 3.0<m/s>)
let v2 = V2D(2.0<m/s>, 3.0<m/s>)
v1 + v2 //yields in FSI: val it : V2D = FSI_0002+V2D {Abs = 7.211102551; Vx = 4.0; Vy = 6.0;}

You can inline them if you want for performance as well, and can define other mathematical functions the same way if you don't want to deal with polymorphism and/or typechecking.

Upvotes: 2

Related Questions