Skip to content

Instantly share code, notes, and snippets.

@diab0l
Last active March 10, 2019 04:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save diab0l/56035eebaab6bca0ac35 to your computer and use it in GitHub Desktop.
Save diab0l/56035eebaab6bca0ac35 to your computer and use it in GitHub Desktop.
// Stuff needed to make C# a kind of cluttered subtyping Haskell:
// - Higher kinded polymorphism (being able to pass Generic Type Definitions as type parameters)
// - Static interfaces & static abstract types (this gets quite perverse at the CLR level)
// - GADTS (easily implemented as syntactic sugar)
namespace HKP {
public static A<B> Create<A, B>() where A : <>, new() {
return new A<B>(); // Can't do a whole lot of useful things here, since we know little about TFn
}
// Assuming arbitrary ctor constraints get implemented
public static A<B> Create<A, B>(B b) where A : <>, new(B) {
return new A<B>(b); // much better
}
// Assuming ctor constraints and subtyping constraints on open types
public static A<C, B> Swap<A, B, C>(A<B, C> abc)
where A : <,>, ITuple<,>, new(B, C)
{
var b = abc.Item1;
var c = abc.Item2;
var acb = new(c, b);
return acb;
}
}
public interface ITuple<T, U> {
T Item1 { get; }
U Item2 { get; }
}
}
namespace StaticInterfaces {
public static interface ILinqyMonad<T> where T : <> {
T<A> Create<A>();
T<B> Select<A, B>(this T<A> ta, Func<A, N> f);
T<B> SelectMany<A, B>(this T<A> ta, Func<A, T<B>> f);
}
public static class Linq : ILinqyMonad<IEnumerable<>> {
public static IEnumerable<A> Create<A>() {
...
}
public static IEnumerable<B> Select<A, B>(this IEnumerable<A> ta, Func<A, N> f) {
...
}
public static IEnumerable<B> SelectMany<A, B>(this IEnumerable<A> ta, Func<A, IEnumerable<B>> f) {
...
}
}
// Static members of interfaces are only useful to constrain generic types
public static class UsageExample {
public static A<B> Foo<T, A, B>()
where T : ILinqyMonad<A>
where A : <>
{
return T.Create<B>();
}
public static void Main() {
IEnumerable<int> lst = Foo<Linq, IEnumerable, int>(); // Type inference would be nice here :(
}
}
}
// Wait, this looks like..
namespace Prelude {
public static interface IFunctor<F>
where F : <> {
F<B> FMap<A, B>(F<A> fa, Func<A, B> f);
}
public static interface IMonad<M> : IFunctor<M>
where M : <> {
M<A> Return<A>(A a);
M<B> Bind<A, B>(M<A> ma, Func<A, M<B>> f);
}
public abstract static class MonadBase<M> : IMonad<M>
where M : <> {
// FMap implemented generally for all monads <3
public static M<B> FMap<A, B>(this M<A> ma, Func<A, B> f)
{
return ma.Bind(a => Return(f(a));
}
public abstract static M<A> Return<A>(A a);
public abstract static M<B> Bind<A, B>(M<A> ma, Func<A, M<B>> f);
}
public data Maybe<T> {
Just(T t);
Nothing;
}
public static class Maybe : MonadBase<Maybe<>> {
// Specialization time
public static override Maybe<B> FMap<A, B>(this M<A> ma, Func<A, B> f) {
switch(ma) {
case Just(a):
return Just(f(b));
default:
return Nothing;
}
}
public static Maybe<A> Return<A>(A a) {
return Just(a);
}
public static Maybe<B> Bind<A, B>(this Maybe<A> ma, Func<A, Maybe<B>> f) {
switch(ma) {
case Just(a):
return f(a);
default:
return Nothing;
}
}
}
}
public class Program {
using Prelude.Maybe; // imports Return, Bind and FMap
using Prelude.Maybe<T>; // imports Just and Nothing
public static void Main() {
Maybe<string> mfoo = Prelude.Maybe.Return<string>("Foo");
Maybe<string> mbar = Prelude.Maybe.Return<string>("Bar");
Maybe<string mfoobar = Prelude.Maybe.Bind<string>(
mfoo,
new Func<string, Maybe<string>>(foo =>
Prelude.Maybe.Bind<string>(
mbar,
new Func<string, Maybe<string>>(
bar =>
Prelude.Maybe<string>.Just(foo + bar)))));
// with type inference
var mx = Return(6);
var my = Return(3);
// results in z === Just(2)
var z = Bind(mx, x =>
Bind(my, y =>
Return(x / y)));
var x = Just(5).Bind(y => Just(y * y))
.Bind(y => y > 5 ? Just(y) : Nothing)
.FMap(y => y.ToString());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment