Add type constraint to F # derived type (this code is not generic enough)

I have this interface:

type IMovingFunc< 'T > = abstract member Add : 'T -> unit 

Now I want to create a generic type that implements the Add function and uses the (+) operator:

 type MovingSum< ^T >(initial : ^T) = let mutable sum = initial interface IMovingFunc< ^T> with member inline this.Add x = sum <- sum + x 

Sorry, I get this error:

This code is not general enough. The type variable ^ T when ^ T: (static term (+): ^ T * ^ T → ^ T) cannot be generalized because it does not hide.

I tried adding a type constraint to MovingSum, but that did not help:

 type MovingSum< ^T when ^T : (static member ( + ) : ^T * ^T -> ^T)>(initial : ^T) 

Could you tell me how to fix this or is there another way to achieve this?

+5
source share
2 answers

As mentioned in Fyodor's comments, types cannot have statically permitted constraints in the same way as functions (mainly because static constraints are handled with inlining) and you cannot embed the whole type).

One way to solve this problem is to make the constraint explicit in the type, and then create a function with a 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 type parameter:

 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 restrictions for members, but when creating MovingSum you need to provide an additional parameter. This is not so bad, but you can avoid this by specifying a built-in function that creates a MovingSum and captures the + operator:

 let inline movingSum initial = MovingSum(initial, fun ab -> a + b) :> IMovingFunc<_> 
+4
source

Thomas's answer is good. However, for completeness: you can use statically permitted type parameters for 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! 

Notice, that:

  • Mandatory restrictions should be included where the parameter is declared.
  • I get some false warnings here, indicating that perhaps this language function is not completely baked ...
  • This will not help you, since the members of the interface cannot be inlined - when setting the type of the interface, the compiler has no way to determine the type of execution that implements the interface, and therefore it is impossible to know which implementation is built-in.
+5
source

Source: https://habr.com/ru/post/1260407/


All Articles