Last active
December 13, 2023 15:58
-
-
Save louthy/93c700c10f61993b9db73a248345cb23 to your computer and use it in GitHub Desktop.
Turn nullable references into an option-monad
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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