Skip to content

Instantly share code, notes, and snippets.

@louthy
Last active December 13, 2023 15:58
Show Gist options
  • Save louthy/93c700c10f61993b9db73a248345cb23 to your computer and use it in GitHub Desktop.
Save louthy/93c700c10f61993b9db73a248345cb23 to your computer and use it in GitHub Desktop.
Turn nullable references into an option-monad
string? name = "Paul";
string? noName = null;
// Only says hello if not null
var helloName = name.Map(n => $"Hello, {name}"); // "Hello, Paul"
var noHelloName = noName.Map(n => $"Hello, {name}"); // null
// Nullable strings ...
string? sx = "Hello";
string? sy = "World";
string? sz = null;
// A test where two nullable strings are used, both are not null
// so the Console.WriteLine shows some output
var r1 = from x in sx
from y in sy
select $"{x}, {y}";
Console.WriteLine(r1); // "Hello, World"
// Test where three nullable strings are used, one is null
// so the Console.WriteLine shows no output.
var r2 = from x in sx
from y in sy
from z in sz
select $"{x}, {y} {z}";
Console.WriteLine(r2); // ""
/// <summary>
/// Extensions for LINQ that turn nullable values into an 'option monad'
///
/// Added Map and Bind for 'functional completeness'
/// </summary>
public static class NullExtensions
{
// Functor 'map' operation
public static B? Map<A, B>(this A? self, Func<A, B> f) =>
self.Select(f);
// Monadic 'bind' operation
public static B? Bind<A, B>(this A? self, Func<A, B?> f) =>
self is null ? default : f(self);
// Functor 'map' operation to make LINQ work
public static B? Select<A, B>(this A? self, Func<A, B> f) =>
self is null ? default : f(self);
// Monadic 'bind' operation to make LINQ work
public static C? SelectMany<A, B, C>(
this A? self,
Func<A, B?> bind,
Func<A, B, C> project) =>
self switch
{
null => default,
var a => bind(a) switch
{
null => default,
var b => project(a, b)
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment