Dunet 1.7.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Dunet --version 1.7.0                
NuGet\Install-Package Dunet -Version 1.7.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="Dunet" Version="1.7.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Dunet --version 1.7.0                
#r "nuget: Dunet, 1.7.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.
// Install Dunet as a Cake Addin
#addin nuget:?package=Dunet&version=1.7.0

// Install Dunet as a Cake Tool
#tool nuget:?package=Dunet&version=1.7.0                

Dunet

Build Package

Dunet is a simple source generator for discriminated unions in C#.

Install

  • NuGet: dotnet add package dunet

Usage

// 1. Import the namespace.
using Dunet;

// 2. Add the `Union` attribute to a partial record.
[Union]
partial record Shape
{
    // 3. Define the union members as inner partial records.
    partial record Circle(double Radius);
    partial record Rectangle(double Length, double Width);
    partial record Triangle(double Base, double Height);
}
// 4. Use the union members.
var shape = new Shape.Rectangle(3, 4);
var area = shape.Match(
    circle => 3.14 * circle.Radius * circle.Radius,
    rectangle => rectangle.Length * rectangle.Width,
    triangle => triangle.Base * triangle.Height / 2
);
Console.WriteLine(area); // "12"

Generics Support

Use generics for more advanced union types. For example, an option monad:

// 1. Import the namespace.
using Dunet;
// Optional: use static import for more terse code.
using static Option<int>;

// 2. Add the `Union` attribute to a partial record.
// 3. Add one or more type arguments to the union record.
[Union]
partial record Option<T>
{
    partial record Some(T Value);
    partial record None();
}
// 4. Use the union members.
Option<int> ParseInt(string? value) =>
    int.TryParse(value, out var number)
        ? new Some(number)
        : new None();

string GetOutput(Option<int> number) =>
    number.Match(
        some => some.Value.ToString(),
        none => "Invalid input!"
    );

var input = Console.ReadLine(); // User inputs "not a number".
var result = ParseInt(input);
var output = GetOutput(result);
Console.WriteLine(output); // "Invalid input!"

input = Console.ReadLine(); // User inputs "12345".
result = ParseInt(input);
output = GetOutput(result);
Console.WriteLine(output); // "12345".

Implicit Conversion Support

Dunet generates implicit conversions between union member values and the union
type if your union meets all of the following conditions:

  • All members contain only a single parameter.
  • All parameters are a different type.
  • No parameters are an interface type.

For example, consider a Result union type that represents success
as a double and failure as an Exception:

// 1. Import the namespace.
using Dunet;

// 2. Define a union type with single unique member values:
[Union]
partial record Result
{
    partial record Success(double Value);
    partial record Failure(Exception Error);
}
// 3. Return union member values directly.
Result Divide(double numerator, double denominator)
{
    if (denominator is 0d)
    {
        // No need for `new Result.Failure(new InvalidOperationException("..."));`
        return new InvalidOperationException("Cannot divide by zero!");
    }

    // No need for `new Result.Success(...);`
    return numerator / denominator;
}

var result = Divide(42, 0);
var output = result.Match(
    success => success.Value.ToString(),
    failure => failure.Error.Message
);

Console.WriteLine(output); // "Cannot divide by zero!"

Async Match Support

Dunet generates a MatchAsync() extension method for all Task<T> and
ValueTask<T> where T is a union type. For example:

// Choice.cs

using Dunet;

namespace Core;

// 1. Define a union type within a namespace.
[Union]
partial record Choice
{
    partial record Yes;
    partial record No(string Reason);
}
// Program.cs

using Core;
using static Core.Choice;

// 2. Define async methods like you would for any other type.
static async Task<Choice> AskAsync()
{
    // Simulating network call.
    await Task.Delay(1000);

    // 3. Return unions from async methods like any other type.
    return new No("because I don't wanna!");
}

// 4. Asynchronously match any union `Task` or `ValueTask`.
var response = await AskAsync()
    .MatchAsync(
        yes => "Yes!!!",
        no => $"No, {no.Reason}"
    );

// Prints "No, because I don't wanna!" after 1 second.
Console.WriteLine(response);

Note: MatchAsync() can only be generated for namespaced unions.

Match on Specific Union Member

Dunet generates specific match methods for each union member. This is useful
when unwrapping a union and you only care about transforming a single variant.
For example:

[Union]
partial record Shape
{
    partial record Point(int X, int Y);
    partial record Line(double Length);
    partial record Rectangle(double Length, double Width);
    partial record Sphere(double Radius);
}
public static bool IsZeroDimensional(this Shape shape) =>
    shape.MatchPoint(
        point => true,
        () => false
    );

public static bool IsOneDimensional(this Shape shape) =>
    shape.MatchLine(
        line => true,
        () => false
    );

public static bool IsTwoDimensional(this Shape shape) =>
    shape.MatchRectangle(
        rectangle => true,
        () => false
    );

public static bool IsThreeDimensional(this Shape shape) =>
    shape.MatchSphere(
        sphere => true,
        () => false
    );

Nested Union Support

To declare a union nested within a class or record, the class or record must
be partial. For example:

// This type declaration must be partial.
public partial class Parent1
{
    // So must this one.
    public partial class Parent2
    {
        // Unions must always be partial.
        [Union]
        public partial record Nested
        {
            public partial record Member1;
            public partial record Member2;
        }
    }
}
// Access union members like any other nested type.
var member1 = new Parent1.Parent2.Nested.Member1();

Samples

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Dunet:

Package Downloads
ZeroC.Slice

Slice for C#.

Xenial.Identity.Client

Package Description

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on Dunet:

Repository Stars
domn1995/dunet
C# discriminated union source generator
icerpc/icerpc-csharp
A C# RPC framework built for QUIC, with bidirectional streaming, first-class async/await, and Protobuf support.
Version Downloads Last updated
1.11.2 46,347 1/29/2024
1.11.1 312 1/27/2024
1.11.0 8,751 1/4/2024
1.11.0-alpha1 348 1/4/2024
1.10.0 10,820 11/2/2023
1.9.0 4,481 9/13/2023
1.8.0 13,766 5/16/2023
1.7.2-pre1 1,428 5/12/2023
1.7.1 7,654 2/17/2023
1.7.0 1,470 1/23/2023
1.6.0 3,079 1/3/2023
1.6.0-preview1 1,176 1/3/2023
1.5.0 11,259 11/13/2022
1.4.2 66,585 11/5/2022
1.4.1 1,502 11/5/2022
1.4.1-preview2 1,407 11/5/2022
1.4.1-preview1 1,391 11/5/2022
1.4.0 2,023 10/6/2022
1.3.0 1,503 9/22/2022
1.2.0 1,802 8/27/2022
1.1.0 3,225 7/11/2022
1.0.2 1,675 7/8/2022
1.0.1 1,649 7/6/2022
1.0.0 1,060 6/27/2022
0.5.0 907 6/26/2022
0.4.0 907 6/26/2022
0.3.0 914 6/24/2022
0.2.2 888 6/18/2022
0.2.1 922 6/16/2022 0.2.1 is deprecated.
0.2.0 926 6/16/2022 0.2.0 is deprecated.
0.1.3 935 6/15/2022 0.1.3 is deprecated.
0.1.2 907 6/13/2022 0.1.2 is deprecated.
0.1.1 938 6/13/2022 0.1.1 is deprecated.
0.1.0 987 6/13/2022 0.1.0 is deprecated because it is no longer maintained.

1.7.0 - Match on specific union member.
1.6.0 - Support generator cancellation.
1.5.0 - Support async Action match functions.
1.4.2 - Disables implicit conversions when union member is an interface type.
1.4.1 - Disable CS1591 in generated code.
1.4.0 - Support Action match functions.
1.3.0 - Async match methods.
1.2.0 - Support nested union declarations.
1.1.0 - Add implicit conversions for union members.
1.0.2 - Support multiple unions with the same name.
1.0.1 - Configure Dunet as development dependency.
1.0.0 - Production release.
0.5.0 - Generate dedicated match method on union records.
0.4.0 - Add support for declaring a union with records.
0.3.0 - Support complex types in union members.
0.2.2 - Add source generator DLL to package.
0.2.1 - Update readme.
0.2.0 - Generate dedicated match method.
0.1.3 - Add support for declaring unions inside top level programs.
0.1.2 - Simplify generated source.
0.1.1 - Support any number of interfaces.
0.1.0 - Initial release.