D20Tek.Functional Migration Guide: Option → Optional

Why the Change?

The Option<T> class served as a good starting point for building a functional class that manages having a value or not, but its naming doesn’t align well with .NET conventions because it already has the concept of Option for defining application settings. Therefore, we’ve introduced a new Optional<T> type that’s cleaner, more consistent, and easier to evolve. However, this change introduces a breaking change to our D20Tek.Functional package.


To address this, we have rolled out these changed is part of a staged migration:

  • 0.9.16 and earlierOption<T> was the only API.
  • 0.9.17 – new Optional<T> class and helpers introduced, Option<T> still works but is marked [Obsolete].
  • 1.0.1 – Breaking change: Option<T> and related types removed entirely.

What Happened in 0.9.17

  • Optional<T> added as the new core type.
  • New factory methods on the static Optional class:
    var some = Optional.Some(42); 
    var none = Optional.None<int>();

  • Added internal SomeOptional<T> / NoneOptional<T> types back for the factories.
    You don’t need to reference them directly (other than as instances of Optional<T>).
  • Legacy API still available but marked [Obsolete]:
    • Option<T>
    • Option static class
    • Some<T>, None<T>
    • ToOption() extensions

What Changed in 1.0.1

  • Removed all Option-based types:
    • Option<T>
    • Option static class
    • Some<T>, None<T> (legacy versions)
    • ToOption() extensions
  • Added public Some<T> / None<T> types that now derive from Optional<T> for full pattern-matching support:
switch (result)
{
    case Some<int> some:
        Console.WriteLine($"Value: {some.Content}");
        break;
    case None<int>:
        Console.WriteLine("No value");
        break;
}

Migration Steps

1. Replace Option<T> with Optional<T>

Before (0.9.16):

Option<string> name = Option.Some("Alice");

After (0.9.17+):

Optional<string> name = Optional.Some("Alice");

2. Update Factory Methods

Before:

var some = Option.Some(99);
var none = Option.None<int>();

After:

var some = Optional.Some(99);
var none = Optional.None<int>();

3. Replace ToOption() with ToOptional()

Before:

var maybeValue = value.ToOption();

After:

var maybeValue = value.ToOptional();

4. Pattern Matching Support

  • In 0.9.17, pattern matching only worked with the old Some<T> / None<T> types.
  • In 1.0.1, Some<T> and None<T> now derive from Optional<T>, so you can match directly on them:
if (opt is Some<int> some)
{
    Console.WriteLine(some.Content);
}

Version Timeline

VersionWhat Happens
0.9.16Option<T> only
0.9.17Optional<T> added, Option<T> deprecated, no breaking changes
1.0.1Option<T> removed, Some<T> / None<T> changed to derive from Optional<T>

Quick Find/Replace Cheatsheet

Old CodeNew Code
Option<T>Optional<T>
Option.Some(value)Optional.Some(value)
Option.None<T>()Optional.None<T>()
value.ToOption()value.ToOptional()
Some<T> / None<T> (0.9.17)Use Optional<T> factories

Final Notes

  • If you’re on 0.9.16, upgrade to 0.9.17 first and migrate to Optional<T> to avoid breakage.
  • If you upgrade straight to 1.0.1, you’ll need to replace all Option references in your code immediately.
  • The new Some<T> / None<T> in 1.0.1 support full C# pattern matching.

Leave a comment