Maybe API Reference

Complete API documentation for the Maybe<T> type and its extensions.

Maybe<T> Structure

1public readonly struct Maybe<T>
2{
3    public bool HasValue { get; }
4    public bool HasNoValue { get; }
5    public T Value { get; }
6}

Factory Methods

From

Creates Maybe from nullable value.

1public static Maybe<T> From(T value)
2
3// Example
4string name = GetName(); // might be null
5Maybe<string> maybeName = Maybe<string>.From(name);

None

Creates empty Maybe.

1public static Maybe<T> None { get; }
2
3// Example
4Maybe<User> noUser = Maybe<User>.None;

Implicit Conversion

1// From value
2Maybe<int> maybe = 42;
3
4// Example
5Maybe<string> name = "John";
6Maybe<string> empty = null; // Becomes None

Transformation Methods

Map

Transforms the value if present.

1public Maybe<TNew> Map<TNew>(Func<T, TNew> mapper)
2
3// Example
4Maybe<string> name = "John";
5Maybe<int> length = name.Map(n => n.Length); // Maybe(4)

Bind

Chains operations that return Maybe.

1public Maybe<TNew> Bind<TNew>(Func<T, Maybe<TNew>> binder)
2
3// Example
4Maybe<User> user = GetUser();
5Maybe<Address> address = user.Bind(u => u.GetPrimaryAddress());

Select (LINQ)

Alias for Map, enables LINQ syntax.

1public Maybe<TNew> Select<TNew>(Func<T, TNew> mapper)
2
3// Example
4var query = from user in maybeUser
5            select user.Name;

SelectMany (LINQ)

Enables LINQ syntax for Bind operations.

1public Maybe<TResult> SelectMany<TNew, TResult>(
2    Func<T, Maybe<TNew>> binder,
3    Func<T, TNew, TResult> projector)
4
5// Example
6var query = from user in maybeUser
7            from profile in user.GetProfile()
8            select new { user.Name, profile.Bio };

Filtering Methods

Where

Filters value based on predicate.

1public Maybe<T> Where(Func<T, bool> predicate)
2
3// Example
4Maybe<int> age = 25;
5Maybe<int> adult = age.Where(a => a >= 18); // Maybe(25)
6Maybe<int> senior = age.Where(a => a >= 65); // None

Side Effect Methods

Tap

Executes action if value exists.

1public Maybe<T> Tap(Action<T> action)
2
3// Example
4maybeUser
5    .Tap(user => Debug.Log($"Found user: {user.Name}"))
6    .Map(user => user.Email);

Execute

Executes action if value exists, returns void.

1public void Execute(Action<T> onValue)
2
3// Example
4maybeConfig.Execute(config => ApplyConfig(config));

ExecuteNoValue

Executes action if no value.

1public void ExecuteNoValue(Action onNoValue)
2
3// Example
4maybeUser.ExecuteNoValue(() => Debug.Log("User not found"));

Pattern Matching

Match

Transforms Maybe to single value.

1public TResult Match<TResult>(
2    Func<T, TResult> onValue,
3    Func<TResult> onNone)
4
5// Example
6string message = maybeUser.Match(
7    onValue: user => $"Hello, {user.Name}",
8    onNone: () => "Hello, Guest"
9);

Match (Async - with NOPE_UNITASK)

Async pattern matching using the same method name.

1public async UniTask<TResult> Match<TResult>(
2    Func<T, UniTask<TResult>> onValue,
3    Func<UniTask<TResult>> onNone)
4
5// Example
6var result = await maybeUser.Match(
7    onValue: async user => await LoadProfileAsync(user),
8    onNone: async () => await CreateGuestProfileAsync()
9);

Value Extraction

Or

Provides default value if None.

1public T Or(T defaultValue)
2public T Or(Func<T> defaultFactory)
3public Maybe<T> Or(Maybe<T> alternative)
4
5// Example
6string name = maybeName.Or("Unknown");
7Config config = maybeConfig.Or(() => LoadDefaultConfig());
8Maybe<User> user = primaryUser.Or(secondaryUser);

GetValueOrThrow

Gets value or throws exception.

1public T GetValueOrThrow()
2public T GetValueOrThrow(string errorMessage)
3
4// Example
5var user = maybeUser.GetValueOrThrow("User is required");

GetValueOrDefault

Gets value or default(T).

1public T GetValueOrDefault()
2
3// Example
4int count = maybeCount.GetValueOrDefault(); // 0 if None

TryGetValue

Attempts to get value with out parameter.

1public bool TryGetValue(out T value)
2
3// Example
4if (maybeUser.TryGetValue(out var user))
5{
6    ProcessUser(user);
7}

Conversion Methods

ToResult

Converts Maybe to Result type.

1public static Result<T, E> ToResult<T, E>(
2    this Maybe<T> maybe,
3    E error)
4
5// Example
6Maybe<User> maybeUser = GetUser();
7Result<User, string> result = maybeUser.ToResult("User not found");
8
9// With custom error type
10public enum UserError { NotFound, InvalidId }
11Result<User, UserError> userResult = maybeUser.ToResult(UserError.NotFound);
12
13// Chain with Result operations
14var finalResult = maybeUser
15    .ToResult("User not found")
16    .Bind(user => ValidateUser(user))
17    .Map(user => user.Profile);

Equality and Comparison

Equals

Value equality comparison.

1public bool Equals(Maybe<T> other)
2public override bool Equals(object obj)
3
4// Example
5Maybe<int> a = 42;
6Maybe<int> b = 42;
7bool equal = a.Equals(b); // true

GetHashCode

1public override int GetHashCode()

Operators

1public static bool operator ==(Maybe<T> left, Maybe<T> right)
2public static bool operator !=(Maybe<T> left, Maybe<T> right)
3
4// Example
5if (maybeA == maybeB) { }

Async Extensions (with NOPE_UNITASK)

Map (Async)

Async transformation using the same method name.

1// Sync Maybe → async transform
2public async UniTask<Maybe<TNew>> Map<TNew>(
3    Func<T, UniTask<TNew>> asyncMapper)
4
5// Async Maybe → async transform
6public static async UniTask<Maybe<TNew>> Map<T, TNew>(
7    this UniTask<Maybe<T>> maybeTask,
8    Func<T, UniTask<TNew>> asyncMapper)
9
10// Example
11Maybe<string> url = GetUrl();
12Maybe<Data> data = await url.Map(async u => await FetchAsync(u));

Bind (Async)

Async chaining using the same method name.

1public async UniTask<Maybe<TNew>> Bind<TNew>(
2    Func<T, UniTask<Maybe<TNew>>> asyncBinder)
3
4// Example
5Maybe<User> user = GetUser();
6Maybe<Profile> profile = await user.Bind(
7    async u => await LoadProfileAsync(u.Id)
8);

Tap (Async)

Async side effects using the same method name.

1public async UniTask<Maybe<T>> Tap(Func<T, UniTask> asyncAction)
2
3// Example
4await maybeUser.Tap(async user => await LogUserAccessAsync(user));

Where (Async)

Async filtering using the same method name.

1public async UniTask<Maybe<T>> Where(Func<T, UniTask<bool>> asyncPredicate)
2
3// Example
4Maybe<User> validUser = await maybeUser.Where(
5    async u => await ValidateUserAsync(u)
6);

Collection Extensions

TryFirst

Gets first element as Maybe.

1public static Maybe<T> TryFirst<T>(this IEnumerable<T> source)
2public static Maybe<T> TryFirst<T>(this IEnumerable<T> source, Func<T, bool> predicate)
3
4// Example
5var users = GetUsers();
6Maybe<User> firstAdmin = users.TryFirst(u => u.IsAdmin);

TryLast

Gets last element as Maybe.

1public static Maybe<T> TryLast<T>(this IEnumerable<T> source)
2public static Maybe<T> TryLast<T>(this IEnumerable<T> source, Func<T, bool> predicate)
3
4// Example
5Maybe<LogEntry> lastError = logs.TryLast(l => l.Level == LogLevel.Error);

Note: TrySingle is not yet implemented. See TODO.md for planned features.

TryFind (Dictionary)

Safe dictionary lookup.

1public static Maybe<TValue> TryFind<TKey, TValue>(
2    this IDictionary<TKey, TValue> dictionary, 
3    TKey key)
4
5// Example
6var settings = GetSettings();
7Maybe<string> apiUrl = settings.TryFind("ApiUrl");

Note: TryElementAt is not yet implemented. See TODO.md for planned features.

Choose

Filters out None values from collection.

1public static IEnumerable<T> Choose<T>(this IEnumerable<Maybe<T>> source)
2
3// Example
4List<Maybe<User>> maybeUsers = GetMaybeUsers();
5List<User> users = maybeUsers.Choose().ToList(); // Only non-None values

Complete Examples

Example 1: User Profile

1public class UserProfileService
2{
3    public string GetUserDisplayName(string userId)
4    {
5        return FindUser(userId)
6            .Bind(user => user.Profile)
7            .Map(profile => profile.DisplayName)
8            .Where(name => !string.IsNullOrWhiteSpace(name))
9            .Or(() => $"User_{userId}");
10    }
11    
12    public Maybe<UserStats> GetUserStats(string userId)
13    {
14        return FindUser(userId)
15            .Bind(user => CalculateStats(user))
16            .Where(stats => stats.IsValid)
17            .Tap(stats => CacheStats(userId, stats));
18    }
19}

Example 2: Configuration

1public class ConfigService
2{
3    private readonly Dictionary<string, string> config;
4    
5    public Maybe<int> GetIntConfig(string key)
6    {
7        return config.TryFind(key)
8            .Bind(value => ParseInt(value))
9            .Where(val => val > 0);
10    }
11    
12    private Maybe<int> ParseInt(string value)
13    {
14        return int.TryParse(value, out var result) 
15            ? Maybe<int>.From(result) 
16            : Maybe<int>.None;
17    }
18}

Example 3: LINQ Usage

1public class InventoryService
2{
3    public Maybe<ItemStack> CraftItem(string recipeId, Inventory inventory)
4    {
5        var result = 
6            from recipe in FindRecipe(recipeId)
7            where HasIngredients(inventory, recipe)
8            from ingredients in GatherIngredients(inventory, recipe)
9            where ConsumeIngredients(inventory, ingredients)
10            from item in CreateItem(recipe)
11            select new ItemStack(item, recipe.OutputQuantity);
12            
13        return result;
14    }
15}

Example 4: Async Operations

1#if NOPE_UNITASK
2public class AsyncUserService
3{
4    public async UniTask<Maybe<UserProfile>> LoadUserProfileAsync(string userId)
5    {
6        return await FindUserIdAsync(userId)
7            .Bind(id => FetchUserFromApiAsync(id))
8            .Where(user => ValidateUserAsync(user))
9            .Map(user => BuildProfileAsync(user))
10            .Tap(profile => CacheProfileAsync(profile));
11    }
12}
13#endif