This limitation of most modern types of systems is well explained in kvb's answer to this question .
A hack based workaround is described here. Actually this is very similar to your code, but less verbose.
type Mul = Mul with static member inline ($) (Mul, a: ^a) = fun (b: ^a) -> a*b type Div = Div with static member inline ($) (Div, a: ^a) = fun (b: ^a) -> a/b type Add = Add with static member inline ($) (Add, a: ^a) = fun (b: ^a) -> a+b type Sub = Sub with static member inline ($) (Sub, a: ^a) = fun (b: ^a) -> ab let inline tup2' fabcd = (f $ a) b, (f $ c) d let b = tup2' Mul 1 2 3.0f 4.0f
The idea is that instead of defining a function, you determine the type using a single method (which you have already done) in this case it will be an operator that will mean application.
So instead of fx you write f $ x .
UPDATE
As already mentioned, your code is not far from the solution suggested in this answer. Here is a working example that is even closer to your source code:
type Mul = Mul with static member inline Op(Mul, a: ^a,b: ^a) = a*b type Div = Div with static member inline Op(Div, a: ^a,b: ^a) = a/b type Add = Add with static member inline Op(Add, a: ^a,b: ^a) = a+b type Sub = Sub with static member inline Op(Sub, a: ^a,b: ^a) = ab let inline op xab = ((^a or ^b): (static member Op: ^a * ^b * ^b -> ^b) (x, a, b)) let inline tup2 abcd = op Mul ab, op Mul cd let inline tup2' fabcd = op fab, op fcd let a = tup2 1 2 3.0f 4.0f let b = tup2' Mul 1 2 3.0f 4.0f //Gives NO type error.
So, basically your source code, but using static methods and using or in restrictions. By doing this, the compiler does not solve the problem earlier and therefore works.
I used the statement because it is less verbose, in which case I like the way it reads, since Haskell $ means application of the function.