Skip to main content

Funcky.DiscriminatedUnion by Polyadic

Nuget / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: Funcky.DiscriminatedUnion

A source generator that generates Match methods for all your discriminated unions needs. ✨

Author: Polyadic

NuGet: https://www.nuget.org/packages/Funcky.DiscriminatedUnion/

You can find more details at https://github.com/polyadic/funcky-discriminated-union

Source : https://github.com/polyadic/funcky-discriminated-union

Original Readme

note

Funcky Discriminated Unions

A source generator that generates Match methods for all your discriminated unions needs. ✨

NuGet package Build Licence: MIT Licence: Apache

Installation

Add <ProjectReference Include="Funcky.DiscriminatedUnion" Version="..." PrivateAssets="all" /> to your project file.

Usage

Apply the [DiscriminatedUnion] to an abstract class (or record) with nested types representing the variants.

Example

using Funcky;

var result = Result<int>.Ok(42);
var resultOrFallback = result.Match(ok: ok => ok.Value, error: _ => 0);

[DiscriminatedUnion]
public abstract partial record Result<T>
where T : notnull
{
public sealed partial record Ok(T Value) : Result<T>;

public sealed partial record Error(Exception Exception) : Result<T>;
}

Minimum Required Versions

  • Visual Studio 2022
  • Roslyn 4.0.0
  • .NET 6

Settings

The attribute allows configuration of some aspects of source generation.

NonExhaustive

The auto-generated Match and Switch methods are public by default. When NonExhaustive is set to true, these methods are generated with internal visibility instead.

MatchResultTypeName

The auto-generated Match method uses a generic type for the result. This type is named TResult by default. This can cause conflict with generic types on the discriminated union itself. Use MatchResultTypeName to set a custom name for this type.

using Funcky;

[DiscriminatedUnion(MatchResultTypeName = "TMatchResult")]
public abstract partial record Result<TResult> { ... }

// Generated code
partial record Result<TResult>
{
public abstract TMatchResult Match<TMatchResult>(...);

...
}

Flatten

The auto-generated Match and Switch methods only accept one level of inheritance by default. Set Flatten to true to include arbitrarily deep inherited types in these methods.

using Funcky;

SyntaxNode node = ...;
var nodeAsString = node.Match(
keyword: keyword => keyword.Value,
integer: integer => integer.Value.ToString(),
double: @double => @double.Value.ToString());

[DiscriminatedUnion(Flatten = true)]
public abstract partial record SyntaxNode
{
public sealed partial record Keyword(string Value) : SyntaxNode;

public abstract partial record Literal : SyntaxNode;

public abstract partial record Number : Literal;

public sealed partial record Integer(int Value) : Number;

public sealed partial record Double(double Value) : Number;
}

[JsonPolymorphic]

System.Text.Json adds support for serializing derived classes starting with .NET 7. This generator supports this feature by generating the required [JsonDerivedType] attributes for you.

All missing [JsonDerivedType] attributes are generated if at least one [JsonDerivedType] or [JsonPolymorphic] attribute is specified.

using Funcky;
using System.Text.Serialization;

[DiscriminatedUnion]
[JsonPolymorphic]
public abstract partial record Shape
{
public sealed partial record Rectangle(double Width, double Length) : Shape;

public sealed partial record Circle(double Radius) : Shape;

public sealed partial record EquilateralTriangle(double SideLength) : Shape;
}
Generated code
using System.Text.Serialization;

[JsonDerivedType(typeof(Rectangle), typeDiscriminator: nameof(Rectangle))]
[JsonDerivedType(typeof(Circle), typeDiscriminator: nameof(Circle))]
[JsonDerivedType(typeof(EquilateralTriangle), typeDiscriminator: nameof(EquilateralTriangle))]
partial record Shape
{
// ...
}

About

note

Generating discriminated unions for C# 9.0 and above.

How to use

Example ( source csproj, source files )

This is the CSharp Project that references Funcky.DiscriminatedUnion

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Funcky.DiscriminatedUnion" Version="1.1.0" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

// <auto-generated/>
#nullable enable

namespace Funcky
{
[global::System.Diagnostics.Conditional("Funcky_DiscriminatedUnion")]
[global::System.AttributeUsage(global::System.AttributeTargets.Class)]
internal sealed class DiscriminatedUnionAttribute : global::System.Attribute
{
/// <summary>Allow only consumers in the same assembly to use the exhaustive <c>Match</c> and <c>Switch</c> methods.</summary>
public bool NonExhaustive { get; set; }

/// <summary>Generates exhaustive <c>Match</c> and <c>Switch</c> methods for the entire type hierarchy.</summary>
public bool Flatten { get; set; }

/// <summary>Customized the generic type name used for the result in the generated <c>Match</c> methods. Defaults to <c>TResult</c>.</summary>
public string? MatchResultTypeName { get; set; }
}
}

Usefull

Download Example (.NET C# )

Share Funcky.DiscriminatedUnion

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Funcky.DiscriminatedUnion

In the same category (FunctionalProgramming) - 10 other generators

cachesourcegenerator

dunet

FunicularSwitch

N.SourceGenerators.UnionTypes

OneOf

PartiallyApplied

RSCG_Utils_Memo

TypeUtilities

UnionGen

UnionsGenerator