ScottEncodingGenerator 1.0.0

dotnet add package ScottEncodingGenerator --version 1.0.0
                    
NuGet\Install-Package ScottEncodingGenerator -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ScottEncodingGenerator" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ScottEncodingGenerator" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ScottEncodingGenerator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ScottEncodingGenerator --version 1.0.0
                    
#r "nuget: ScottEncodingGenerator, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ScottEncodingGenerator@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ScottEncodingGenerator&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ScottEncodingGenerator&version=1.0.0
                    
Install as a Cake Tool

ScottEncodingGenerator

ScottEncodingGenerator is a C# source generator for defining closed sets of nested case types and generating a Match API for them.

It targets types marked with [ScottEncoding] and generates:

  • a Match<TResult>(...) member on the target type
  • per-case Match implementations on nested case types
  • factory methods for constructing cases
  • step-by-step extension methods for building a full match expression

The generator supports:

  • partial interface targets
  • abstract partial class targets

It does not support:

  • structs as targets
  • generic nested cases
  • non-partial nested cases

Purpose

The generator is intended for code shaped like a discriminated union encoded with nested types.

Instead of manually writing:

  • the common Match contract
  • one implementation per case
  • helper factory methods
  • fluent matching helpers

the generator derives those from the target type and its nested case declarations.

Example

Input

[ScottEncoding]
public abstract partial class Option<T>
{
    public sealed partial class Some
    {
        public Some(T value) => Value = value;
        public T Value { get; }
    }

    public sealed partial class None
    {
        public None() { }
    }
}

Generated shape

The generator adds a Match member to the target:

public abstract partial class Option<T>
{
    public abstract TResult Match<TResult>(
        Func<Option<T>.Some, TResult> some,
        Func<Option<T>.None, TResult> none);

    public sealed partial class Some : Option<T>
    {
        public override TResult Match<TResult>(
            Func<Option<T>.Some, TResult> some,
            Func<Option<T>.None, TResult> none)
            => some(this);
    }

    public sealed partial class None : Option<T>
    {
        public override TResult Match<TResult>(
            Func<Option<T>.Some, TResult> some,
            Func<Option<T>.None, TResult> none)
            => none(this);
    }
}

It also generates a companion helper type:

public static partial class OptionModule
{
    public static Option<T> Some<T>(T value) => new Option<T>.Some(value);
    public static Option<T> None<T>() => new Option<T>.None();

    public static MatchStep2<T, TResult> IfSome<T, TResult>(
        this Option<T> value,
        Func<Option<T>.Some, TResult> some)
        => new(value, some);
}

Usage:

Option<int> x = OptionModule.Some(10);

var text = x.Match(
    some => $"Some({some.Value})",
    none => "None");

Or with the generated step API:

var text = x
    .IfSome(some => $"Some({some.Value})")
    .IfNone(_ => "None");

Interface targets

The generator also supports interface targets.

Input

[ScottEncoding]
public partial interface Expr
{
    public sealed partial class Constant
    {
        public Constant(int value) => Value = value;
        public int Value { get; }
    }

    public sealed partial class Add
    {
        public Add(Expr left, Expr right)
        {
            Left = left;
            Right = right;
        }

        public Expr Left { get; }
        public Expr Right { get; }
    }
}

Generated shape

For interfaces, the generator emits:

  • a Match<TResult>(...) declaration on the interface
  • nested case classes implementing the interface
  • helper factories in ExprModule

Example use:

Expr expr = ExprModule.Add(
    ExprModule.Constant(1),
    ExprModule.Constant(2));

var value = expr.Match(
    constant => constant.Value,
    add => add.Left.Match(
               l => l.Value,
               _ => 0) +
           add.Right.Match(
               r => r.Value,
               _ => 0));

Rules

A target type must:

  • be marked with [ScottEncoding]

  • be partial

  • be either:

    • an abstract partial class, or
    • a partial interface

A nested case type must:

  • be declared inside the target
  • be a non-generic class
  • be partial
  • be non-abstract
  • expose at least one accessible instance constructor

For class targets, a nested case may:

  • omit an explicit base type, or
  • inherit from the target type

For interface targets, a nested case may:

  • omit an explicit base type, or
  • implement the target interface

A nested case must not specify an unrelated base type.

Factories

For each accessible constructor on each case, the generator emits a factory method in <TargetName>Module.

Example:

public sealed partial class Error
{
    public Error(string message) => Message = message;
    public string Message { get; }
}

Generates a factory similar to:

public static Result<T> Error<T>(string message) => new Result<T>.Error(message);

If a case has multiple accessible constructors, additional factory methods are emitted with suffixes derived from constructor parameters or an index.

Matching API

The generated Match<TResult>(...) method takes one delegate per case, in declaration order.

For a target with cases:

  • Success
  • Failure
  • Loading

the generated signature is shaped like:

TResult Match<TResult>(
    Func<Success, TResult> success,
    Func<Failure, TResult> failure,
    Func<Loading, TResult> loading);

The fluent step API follows the same order:

value
    .IfSuccess(...)
    .IfFailure(...)
    .IfLoading(...);

Diagnostics

The generator reports the following diagnostics.

SCOTT001

Target is not valid.

Raised when the target is not:

  • partial
  • an interface
  • or an abstract class

SCOTT002

No valid cases were found.

Raised when the target does not declare any usable nested case types.

SCOTT003

A nested case has an invalid shape.

Raised when a case is not a:

  • partial class
  • non-generic class
  • non-abstract class

SCOTT004

A nested case has no accessible instance constructor.

SCOTT005

A nested case has an invalid relationship to the target.

Raised when a case declares an unrelated base type.

SCOTT006

The target already declares a conflicting Match method.

Generated files

The generator emits:

  • ScottEncodingAttribute.g.cs
  • one generated file per valid target, named from the target symbol

The generated code enables nullable references with:

#nullable enable

Notes

  • Cases are processed in a stable order based on syntax location.
  • The helper type is named <TargetName>Module.
  • The helper type is generated as static partial.
  • Matching delegates use fully qualified type names in generated code.
  • The generated API preserves target type parameters and constraint clauses.

Minimal example

[ScottEncoding]
public abstract partial class BoolLike
{
    public sealed partial class True
    {
        public True() { }
    }

    public sealed partial class False
    {
        public False() { }
    }
}

Usage:

BoolLike value = BoolLikeModule.True();

var result = value.Match(
    @true => 1,
    @false => 0);
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 116 4/3/2026