A minimalist, dependency-free Option<T> implementation for C#. Type-safe optional values with functional programming patterns.
Fox.OptionKit provides a clean, lightweight implementation of the Option pattern that brings functional programming safety to C# without any dependencies:
- Zero Dependencies - No external dependencies, not even Microsoft.Extensions.*
- Type-Safe - Eliminate NullReferenceExceptions with explicit optional types
- Functional - Map, Bind, Match operations for clean composition
- Lightweight - Minimal overhead, simple API, fast compilation
- Well-Documented - Comprehensive XML documentation for IntelliSense
- Testable - Immutable struct with predictable behavior
dotnet add package Fox.OptionKitusing Fox.OptionKit;
// Create Some (value present)
var some = Option.Some(42);
var someString = Option.Some("Hello");
// Create None (value absent)
var none = Option.None<int>();
// From nullable types
string? nullable = GetNullableString();
Option<string> option = nullable.ToOption();// Match pattern
var result = option.Match(
some: value => $"Found: {value}",
none: () => "Not found");
// Check state
if (option.HasValue)
{
Console.WriteLine($"Value: {option.Value}");
}
// Get value or default
var value = option.ValueOr("default");// Map: transform value if present
var doubled = Option.Some(21)
.Map(x => x * 2); // Some(42)
// Bind: chain operations that return Option
var userId = Option.Some("user@example.com")
.Bind(email => FindUserByEmail(email))
.Bind(user => GetUserId(user));
// Combine operations
var greeting = Option.Some("World")
.Map(name => $"Hello, {name}!")
.ValueOr("Hello, stranger!");Eliminate null reference exceptions with explicit optional types:
// Instead of nullable reference
string? name = GetName(); // Might be null
Console.WriteLine(name.Length); // NullReferenceException!
// Use Option<T>
Option<string> name = GetName().ToOption();
Console.WriteLine(name.Match(
some: n => $"Length: {n.Length}",
none: () => "No name"));Chain operations cleanly with Map and Bind:
// Map: transform value inside Option
var result = Option.Some("hello")
.Map(s => s.ToUpper()) // Some("HELLO")
.Map(s => s.Length); // Some(5)
// Bind: chain operations that return Option
var user = Option.Some("user@example.com")
.Bind(email => FindUser(email)) // Option<User>
.Bind(user => GetUserProfile(user)); // Option<Profile>Handle both Some and None cases explicitly:
var message = config.GetValue("ApiKey").Match(
some: key => $"API Key configured: {key}",
none: () => "API Key missing - using defaults");
// Or use HasValue/IsNone
if (maybeUser.HasValue)
{
ProcessUser(maybeUser.Value);
}Convert nullable types to Options:
// From nullable reference types
string? nullableString = GetString();
Option<string> option = nullableString.ToOption();
// From nullable value types
int? nullableInt = GetInt();
Option<int> option = nullableInt.ToOption();
// Safe dictionary access
var value = dictionary.TryGetValue("key", out var v)
? Option.Some(v)
: Option.None<string>();Fox.OptionKit is built around a simple, immutable Option<T> struct:
Option<T>
├── Some(value) → HasValue = true, IsNone = false
└── None() → HasValue = false, IsNone = true
Core operations:
Some(T)/None()- Create optionsMatch<TResult>- Pattern matchingMap<TResult>- Transform value (Functor)Bind<TResult>- Chain operations (Monad)ValueOr(T)- Safe value extraction
Fox.OptionKit is ideal for:
- API Responses - Return optional data without null checks
- Configuration Values - Handle missing config gracefully
- Database Queries - Represent "not found" explicitly
- Parsing Operations - Return Some(result) or None for invalid input
- Domain Models - Model optional properties type-safely
- Service Results - Eliminate null returns from service methods
Fox.OptionKit is designed for zero-overhead abstraction:
- Zero Allocations -
Option<T>is a readonly struct, no heap allocations - Zero Dependencies - No external dependencies means minimal assembly size
- Inline-Friendly - Simple methods can be inlined by JIT compiler
- No Boxing - Value types stay on the stack throughout the pipeline
| Feature | Fox.OptionKit | LanguageExt | Optional | C# Nullable |
|---|---|---|---|---|
| Zero Dependencies | ✅ | ❌ | ✅ | ✅ (built-in) |
| Struct (value type) | ✅ | ❌ (class) | ✅ | ✅ |
| Map/Bind/Match | ✅ | ✅ | ✅ | ❌ |
| Extension Methods | ✅ | ✅ | ✅ | ❌ |
| Static Factory | ✅ | ✅ | ✅ | N/A |
| Null Safety | ✅ | ✅ | ✅ | |
| Learning Curve | Low | High | Low | None |
| Multi-targeting | .NET 8-10 | .NET 6+ | .NET Standard | Built-in |
A comprehensive sample application is available in the repository demonstrating:
- ✅ Basic Option creation (Some/None)
- ✅ Pattern matching with Match
- ✅ Functional composition with Map and Bind
- ✅ Value extraction with ValueOr
- ✅ Nullable conversion with ToOption
- ✅ Real-world examples (dictionary lookups, safe access)
Run the sample:
cd samples/Fox.OptionKit.Demo
dotnet runExplore:
- View Program.cs for complete examples
- Zero Dependencies - No external dependencies, not even Microsoft.Extensions.*
- Type-Safe - Explicit optional values, no null references
- Immutable - Readonly struct, all operations return new instances
- Simple - Small API surface, easy to learn and use
- Functional - Map, Bind, Match operations following functor/monad laws
- Performance - Zero allocations, inline-friendly, minimal overhead
- .NET 8.0 or higher
- C# 12 or higher (for modern language features)
- Nullable reference types enabled (recommended)
Fox.OptionKit is intentionally lightweight and feature-focused. The goal is to remain a simple library with zero dependencies for the Option pattern.
- ✅ Bug fixes - Issues with existing functionality
- ✅ Documentation improvements - Clarifications, examples, typo fixes
- ✅ Performance optimizations - Without breaking API compatibility
- ✅ New features - Following existing patterns and SOLID principles
- ❌ Any external dependencies (this library must remain dependency-free)
- ❌ Large feature additions that increase complexity significantly
- ❌ Breaking API changes without strong justification
If you want to propose a significant change, please open an issue first to discuss whether it aligns with the project's philosophy.
The project enforces a strict build policy to ensure code quality:
- ❌ No errors allowed - Build must be error-free
- ❌ No warnings allowed - All compiler warnings must be resolved
- ❌ No messages allowed - Informational messages must be suppressed or addressed
All pull requests must pass this requirement.
Fox.OptionKit follows strict coding standards:
- Comprehensive unit tests required (xUnit + FluentAssertions)
- Maximum test coverage required - Aim for 100% line and branch coverage. Tests may only be omitted if they would introduce artificial complexity (e.g., testing unreachable code paths, framework internals, or compiler-generated code). Use
[ExcludeFromCodeCoverage]sparingly and only for justified cases. - XML documentation for all public APIs - Clear, concise documentation with examples
- Follow Microsoft coding conventions - See
.github/copilot-instructions.mdfor project-specific style
- Follow the existing code style (see
.github/copilot-instructions.md) - Use file-scoped namespaces
- Enable nullable reference types
- Use expression-bodied members for simple properties/methods
- Private fields: camelCase without underscore prefix
- Add XML documentation decorators (98-character width)
- Fork the repository
- Create a feature branch from
main - Follow the coding standards in
.github/copilot-instructions.md - Write comprehensive unit tests (aim for 100% coverage)
- Ensure all tests pass and build is clean (zero warnings/errors)
- Submit a pull request
See CONTRIBUTING.md for detailed guidelines.
This project is licensed under the MIT License - see the LICENSE.txt file for details.
Károly Akácz
- GitHub: @akikari
- Repository: Fox.OptionKit
See CHANGELOG.md for version history.
- Fox.ResultKit - Lightweight Result pattern library for Railway Oriented Programming
- Fox.ChainKit - Chain of Responsibility pattern implementation
- Fox.ConfigKit - Type-safe configuration validation library for .NET
For issues, questions, or feature requests, please open an issue in the GitHub repository.