C# Static interfaces - Take 3
You may believe it or not, but the post that drains most of the traffic of this blog, is the one about C# static interfaces !
In october 2009, I simply tried to imagine where the idea of C# static interfaces could lead us, and, since then, I have more viewed pages (> 15%) on this post than on my home page !
And since then, nothing moved in this area in the C# langage, and I don’t expect it to happen soon.
But some other thing happened…
F#
Yes F# is out and running on almost all platforms, and it can do what I described in the previous post.
The thing is called Statically Resolved Type Parameters and is closer to C++ templates than from C# generics.
The trick is that you can define an inline function with statically resolved types, denoted by a ^ prefix. The usage of defined methods on the type is not given here by an interface, but by a constraint on the resolved type :
let inline count (counter: ^T) = let value = (^T: (member Count : int) counter) value
here , the count function takes a counter of type ^T (statically resolved).
The second line express that ^T actually should have a member Count of type int, and that it will call it on counter to get the result value !
Magic !
Now, we can call count on various types that have a Count member property like :
type FakeCounter() = member this.Count = 42;
or
type ImmutableCounter(count: int) = member this.Count = count; member this.Next() = ImmutableCounter(count + 1)
or
type MutableCounter(count: int) = let mutable count = 0 member this.Count = count; member this.Next() = count <- count + 1
without needing an interface !
For instance :
let c = count (new FakeCounter())
True, this is compile time duck typing !
And it works with methods :
let inline quack (duck: ^T) = let value = (^T: (member Quack : int -> string) (duck, 3)) value
This will call a Quack method that takes int and returns string with the value 3 on any object passed to it that has a method corresponding to the constraint.
And magically enough, you can do it with static methods :
let inline nextThenstaticCount (counter: ^T) = (^T: (member Next : unit -> unit) counter) let value = (^T: (static member Count : int) ()) value
this function calls an instance method called Next, then gets the value of a static property called Count and returns the value !
It also works with operators :
let inline mac acc x y = acc + x * y
notice the signature of this function :
acc: ^a -> x: ^c -> y: ^d -> ^e when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^e) and ( ^c or ^d) : (static member ( * ) : ^c * ^d -> ^b)
It accepts any types as long as they provide expected + and * operators.
The only thing is that a specific implementation of the function will be compiled for each type on which it’s called. That’s why it called statically resolved.
You can use this kind of method from F# code but not from C#.
Anyway…
No need for static interfaces in C#, use F# !