Burritos in C#

Wooga.Lambda

Functional primitives for Unity3D

inspired by csharp-monads, Haskell & F#

compatible with Unity3D

Why functional?

simplicity

predictability

composability

safety

concurrency

Problems

problem: null

string FirstOf(string[] xs)
{
  return xs.Length > 0 ? xs[0] : null;
}

var name = FirstOf(names);
var letters = name.Length; // Causes NullReferenceException

solution: maybe

"The Maybe type encapsulates an optional value. A value of type Maybe[T] either contains a value of type T (Just[T]), or it is empty (Nothing[T])."

example: maybe

string FirstOf(string[] xs)
{
  return xs.Length > 0 ? xs[0] : null;
}

var name = FirstOf(names);
var letters = name.Length; // Causes NullReferenceException

// vs.

Maybe<string> FirstOf(string[] xs)
{
  return xs.Length > 0 ? Maybe.Just(xs[0]) : Maybe.Nothing<string>();
}

var name = FirstOf(names);
var letters = name.FromJustOrDefault(0,s => s.Length);

composing: maybe

Maybe<string> FirstOf(string[] xs)
{
    return xs.Length > 0 ? Maybe.Just(xs[0]) : Maybe.Nothing<string>();
}

Maybe<string> StartsWithA(string s)
{
    return s.StartsWith("A") ? Maybe.Just(s) : Maybe.Nothing<string>();
}

Maybe<string> AtLeast5Chars(string s)
{
    return s.Length >= 5 ? Maybe.Just(s) : Maybe.Nothing<string>();
}

var letters = FirstOf(names)
              .Bind(StartsWithA)
              .Bind(AtLeast5Chars)
              .FromJustOrDefault(0,s => s.Length);

problem: exceptions

bool ValidPassword(string x)
{
    if(x.Length < 8)
        throw new System.Exception("Password needs at least 8 chars");
    if(x.Contains("-"))
        throw new System.Exception("Password needs to contain a dash");
    return true;
}

var valid = false;
try
{
    valid = ValidPassword(pw);
}
catch (Exception e)
{
    Console.WriteLine("Validation error:"+e);
}
if(valid) Console.WriteLine("Validation succeeded");

solution: either

"The Either type represents values with two possibilities: a value of type Either[T1,T2] is either Left[T1] or Right[T2]."

"The Either type is sometimes used to represent a value which is either correct or an error; by convention, Left is used to hold an error value and Right holds a correct value"

example: either

Either<Exception,string> ValidPassword(string x)
{
    if(x.Length < 8)
        return Either.Left<Exception,string>(new Exception("Password needs at least 8 chars"));
    if (x.Contains("-"))
        return Either.Left<Exception, string>(new Exception("Password needs to contain a dash"));
    return Either.Right<Exception,string>(x);
}

var valid = ValidPassword(pw);
valid.IsLeft()
    ? Console.WriteLine("Validation error:" + valid.FromLeft())
    : Console.WriteLine("Validation succeeded");

composing: either

Either<Exception, string> AtLeast8Chars(string x)
{
    return x.Length < 8
        ? Either.Left<Exception, string>(new Exception("Password needs at least 8 chars"))
        : Either.Right<Exception, string>(x);
}

Either<Exception, string> ContainsDash(string x)
{
    return x.Contains("-")
        ? Either.Left<Exception, string>(new Exception("Password needs to contain a dash"))
        : Either.Right<Exception, string>(x);
}

Either<Exception,string> ValidPassword(string x)
{
    return AtLeast8Chars(x)
          .Bind(ContainsDash);
}

var valid = ValidPassword(pw);
valid.IsLeft()
    ? Console.WriteLine("Validation error:" + valid.FromLeft())
    : Console.WriteLine("Validation succeeded");

problem: concurrency

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(FinishWebRequest, null);
}

void FinishWebRequest(IAsyncResult result)
{
    var response = webRequest.EndGetResponse(result);
    Console.WriteLine(response.ContentLength);
}

StartWebRequest(); // How do I get the response out?!?

solution: async

"A compositional asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception." - F# docs

example: async

Async<string> LoadHugeJsonAsync(Uri uri)
{
    return () => "....json..."; //This could be a synchronous webrequest
}

var req = LoadHugeJsonAsync(new Uri("huge-json.com"));

// Run it synchronously/blocking
var result = req.RunSynchronously();
Console.WriteLine(result);

// Run it async with callback
var callback = new AsyncReplyChannel<string>(s =>
{
    Console.WriteLine(s);
    return Unit.Default;
});
req.StartAndReply(ch => callback);

composing: async

Async<string> LoadHugeJsonAsync(Uri uri)
{
    return () => "....json..."; //This could be a synchronous webrequest
}

Async<Dictionary<string, string>> ParseHugeJsonAsync(string json)
{
    return () => new Dictionary<string, string>(); // There should be some parsing here
}

var req = LoadHugeJsonAsync(new Uri("huge-json.com"))
          .Bind(ParseHugeJsonAsync);

// Get the dictionary synchronously
var dict = req.RunSynchronously();

composing: async (parallel)

Async<string> LoadHugeJsonAsync(Uri uri)
{
    return () => "....json..."; //This could be a synchronous webrequest
}

var reqs = new []
{
    LoadHugeJson(new Uri("huge-json.com/1")),
    LoadHugeJson(new Uri("huge-json.com/2")),
    LoadHugeJson(new Uri("huge-json.com/3"))
};

var results = reqs.Parallel().RunSynchronously();
foreach (var result in results)
{
    Console.WriteLine(result);
}

Thanks

github.com/wooga/Wooga.Lambda-CSharp