Reputation: 1129
I have this interface:
type IMovingFunc< 'T > =
abstract member Add : 'T -> unit
Now I want to create a generic type that implements Add function and uses (+) operator:
type MovingSum< ^T >(initial : ^T) =
let mutable sum = initial
interface IMovingFunc< ^T> with
member inline this.Add x = sum <- sum + x
Unfortunately, I'm getting this error:
This code is not sufficiently generic. The type variable ^T when ^T : (static member ( + ) : ^T * ^T -> ^T) could not be generalized because it would escape its scope.
I've tried adding type constraint to MovingSum but it didn't help:
type MovingSum< ^T when ^T : (static member ( + ) : ^T * ^T -> ^T)>(initial : ^T)
Could you please tell me how to fix this or is there other way to achieve this?
Upvotes: 6
Views: 1444
Reputation: 55184
Tomas's answer is good. However, for completeness: it is possible to use statically resolved type parameters on types:
type MovingSum< ^T when ^T : (static member (+) : ^T * ^T -> ^T)>(initial : ^T) =
let mutable sum = initial
member inline this.Add(x:^T) = sum <- sum + x
let m = MovingSum(1) in m.Add(3) // works!
But note that:
Upvotes: 7
Reputation: 243041
As mentioned by Fyodor in the comments, types cannot have statically resolved constraints in the same way as functions (mainly because static constraints are handled using inlining - and you cannot really inline a whole type).
One way to address this is to make the constraint explicit in the type and then create a function with static member constraint that captures the functionality and passes it to the type.
In your example, you need the +
operator, so we can add adder
as a parameter of the type:
type MovingSum<'T>(initial:'T, adder:'T -> 'T -> 'T) =
let mutable sum = initial
interface IMovingFunc<'T> with
member this.Add x = sum <- adder sum x
This does not require static member constraints, but you need to provide the extra parameter when creating MovingSum
. This is not too bad, but you can avoid that by defining an inline function that creates MovingSum
and captures the +
operator:
let inline movingSum initial =
MovingSum(initial, fun a b -> a + b) :> IMovingFunc<_>
Upvotes: 6