A recent C# feature is static interface members – this opens up some new possibilities for bending C# to make trait like functionality work. You may have already seen the technique:
public interface Addable<SELF> where SELF : Addable<SELF>
{
public static abstract SELF Add(SELF x, SELF y);
}
Note, how the Add member is static abstract and that the interface has a constraint that SELF is forced to inherit Addable<SELF>.
We can then create two distinct types that inherit the Addable trait:
public record MyList<A>(A[] values) : Addable<MyList<A>>
{
public static MyList<A> Add(MyList<A> x, MyList<A> y) =>
new (x.values.Append(y.values).ToArray());
}
public record MyString(string value) : Addable<MyString>
{
public static MyString Add(MyString x, MyString y) =>
new (x.value + y.value);
}
Language-Ext takes this idea and uses it to implement 'higher-kinded traits' (with the K<F, A> type being the anchor for
them all).
To continue reading about how this works, check out Paul Louth's Higher-Kinds series.
Contents
Sub modules
interface K <in F, A> Source #
Arrow kind: * -〉* used to represent higher-kinded types.
K〈F, A〉 should be thought of as F〈A〉 (where both F an A are parametric). It currently
can't be represented in C#, so this allows us to define higher-kinded types and pass them
around. We can then build traits that expected a K where the trait is tied to the F.
For example:
K〈F, A〉 where F : Functor〈F〉
K〈M, A〉 where M : Monad〈M〉
That means we can write generic functions that work with monads, functors, etc.
Parameters
| type | F | Trait type |
| type | A | Bound value type |
interface K <in F, P, A> Source #
Arrow kind: * -〉* -〉* used to represent higher-kinded types.
Parameters
| type | F | Trait type |
| type | P | Alternative value type |
| type | A | Bound value type |
class KExtensions Source #
Methods
method K<F, A> Kind <F, A> (this K<F, A> fa) Source #
Get the base kind type. Avoids casts mid-expression
method K<M, K<N, A>> KindT <M, N, NA, A> (this K<M, NA> mna) Source #
KindT converts a nested Kind type (inherits K〈M, A〉), where the inner
type is a concrete type and not K〈N, A〉 to the more general version - which
allows the other T variant methods to work seamlessly.
The casting of nested types is especially problematic for C#'s type-system,
so even though this isn't ideal, it does allow for a truly generic system
of working with any nested types as long as there's a Functor implementation
for the outer type.
Parameters
| type | M | Outer functor trait (i.e. |
| type | N | Inner trait (i.e. |
| type | NA | Concrete nested type (i.e. |
| type | A | Concrete bound value type (i.e. |
| param | mna | Nested functor value |
| returns | More general version of the type that can be used with other | |
Examples
var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3));
var ma = mx.KindT〈Seq, Option, Option〈int〉, int〉()
.BindT(a => Some(a + 1))
.MapT(a => a + 1);
.AsT〈Seq, Option, Option〈int〉, int〉();
method K<M, NA> AsT <M, N, NA, A> (this K<M, K<N, A>> mna) Source #
AsT converts a nested Kind type (inherits K〈M, A〉), where the inner type
is a general type (K〈N, A〉) to its downcast concrete version.
The casting of nested types is especially problematic for C#'s type-system,
so even though this isn't ideal, it does allow for a truly generic system
of working with any nested types as long as there's a Functor implementation
for the outer type.
Parameters
| type | M | Outer functor trait (i.e. |
| type | N | Inner trait (i.e. |
| type | NA | Concrete nested type (i.e. |
| type | A | Concrete nested-monad bound value type (i.e. |
| param | mna | Nested functor value |
| returns | Concrete version of the general type. | |
Examples
var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3));
var ma = mx.KindT〈Seq, Option, Option〈int〉, int〉()
.BindT(a => Some(a + 1))
.MapT(a => a + 1);
.AsT〈Seq, Option, Option〈int〉, int〉();
class TraitAttribute Source #
Fields
field string NameFormat Source #
Constructors
constructor TraitAttribute (string nameFormat) Source #