Dusharp by Vitali
Nuget / site data
Details
Info
Name: Dusharp
Dusharp is a C# source generator for creating discriminated unions.
Author: Vitali
NuGet: https://www.nuget.org/packages/Dusharp/
You can find more details at https://github.com/kolebynov/Dusharp
Source : https://github.com/kolebynov/Dusharp
Original Readme
Dusharp
Dusharp is a C# source generator library for creating discriminated unions. This library allows you to define union types with ease, using attributes and partial methods. It is inspired by functional languages but built for C# developers.
Features
- ✅ Create unions: Define discriminated unions using attributes.
- ✅ Match method: Pattern match on union cases in a type-safe way.
- ✅ Equality: Automatic equality comparison for unions.
- ✅ Generics: Generics support for union types.
- ✅ Pretty print: Using overloaded
ToString()
. - ❌ JSON serialization/deserialization: Support for unions with
System.Text.Json
(coming soon). - ❌ Struct unions: With efficient memory layout for unions as structs (coming soon).
Installation
Dusharp is available as a NuGet package. You can install it using the NuGet package manager:
dotnet add package Dusharp
Usage
Dusharp
uses attributes to generate discriminated unions and case methods. Here's how to get started:
1. Define a Union
To define a union, annotate a class with the [Dusharp.UnionAttribute]
attribute.
using Dusharp;
[Union]
public partial class Shape<T>
where T : struct, INumber<T>
{
}
2. Define Union Cases
Define union cases by creating public static partial methods and marking them with the [Dusharp.UnionCaseAttribute]
attribute. The method body will be automatically generated.
using Dusharp;
[Union]
public partial class Shape<T>
where T : struct, INumber<T>
{
[UnionCase]
public static partial Shape<T> Circle(T radius);
[UnionCase]
public static partial Shape<T> Rectangle(T width, T height);
}
3. Match on Union
You can easily perform pattern matching on a union using the Match
method. The source generator will create the Match
method based on the defined union cases.
Shape<double> shape = Shape<double>.Circle(5.0);
string result = shape.Match(
radius => $"Circle with radius {radius}",
(width, height) => $"Rectangle with width {width} and height {height}");
Console.WriteLine(result); // Output: Circle with radius 5.0
4. Compare Unions
Union cases can be compared for equality using the auto-generated equality methods. This allows for checking if two unions are the same.
Shape<double> shape1 = Shape<double>.Circle(5.0);
Shape<double> shape2 = Shape<double>.Circle(5.0);
Console.WriteLine(shape1.Equals(shape2)); // True
Console.WriteLine(shape1 == shape2); // True
Upcoming Features
- JSON serialization/deserialization: Support for JSON (de)serialization via System.Text.Json.
- Struct unions: More efficient unions using structs with effective data layout.
License
This project is licensed under the MIT License - see the LICENSE file for details.
About
Generate tagged union
How to use
Example ( source csproj, source files )
- CSharp Project
- Program.cs
- ResultSave.cs
- SaveToDatabase.cs
This is the CSharp Project that references Dusharp
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dusharp" Version="0.4.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
This is the use of Dusharp in Program.cs
using UnionTypesDemo;
Console.WriteLine("Save or not");
var data = SaveToDatabase.Save(0);
data.Match(
ok => Console.WriteLine(ok),
()=> Console.WriteLine("Not found")
);
data = SaveToDatabase.Save(1);
data.Match(
ok => Console.WriteLine(ok),
() => Console.WriteLine("Not found")
);
This is the use of Dusharp in ResultSave.cs
using Dusharp;
namespace UnionTypesDemo;
[Union]
public partial class ResultSave
{
[UnionCase]
public static partial ResultSave Ok(int i);
[UnionCase]
public static partial ResultSave NotFound();
}
This is the use of Dusharp in SaveToDatabase.cs
namespace UnionTypesDemo;
public class SaveToDatabase
{
public static ResultSave Save(int i)
{
if (i == 0)
{
return ResultSave.NotFound();
}
return ResultSave.Ok(i); ;
}
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- Dusharp.EmbeddedCode.ExceptionUtils.cs
- Dusharp.EmbeddedCode.UnionAttribute.cs
- Dusharp.EmbeddedCode.UnionCaseAttribute.cs
- UnionTypesDemo.ResultSave.Dusharp.g.cs
// <auto-generated> This file has been auto generated. </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;
namespace Dusharp
{
public static class ExceptionUtils
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThrowIfNull<T>(this T value, string paramName)
where T : class
{
if (value == null)
{
ThrowArgumentNull(paramName);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowUnionInInvalidState() =>
throw new InvalidOperationException("Union in invalid state.");
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowArgumentNull(string paramName) => throw new ArgumentNullException(paramName);
}
}
// <auto-generated> This file has been auto generated. </auto-generated>
#nullable enable
using System;
namespace Dusharp
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class UnionAttribute : Attribute
{
}
}
// <auto-generated> This file has been auto generated. </auto-generated>
#nullable enable
using System;
namespace Dusharp
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class UnionCaseAttribute : Attribute
{
}
}
// <auto-generated> This file has been auto generated. </auto-generated>
#nullable enable
namespace UnionTypesDemo
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("", "CA1000", Justification = "For generic unions.")]
abstract partial class ResultSave : System.IEquatable<ResultSave>
{
private ResultSave() {}
public void Match(System.Action<int> okCase, System.Action notFoundCase)
{
Dusharp.ExceptionUtils.ThrowIfNull(okCase, "okCase");
Dusharp.ExceptionUtils.ThrowIfNull(notFoundCase, "notFoundCase");
{
var unionCase = this as OkCase;
if (!object.ReferenceEquals(unionCase, null)) { okCase(unionCase.i); return; }
}
{
var unionCase = this as NotFoundCase;
if (!object.ReferenceEquals(unionCase, null)) { notFoundCase(); return; }
}
Dusharp.ExceptionUtils.ThrowUnionInInvalidState();
}
public TRet Match<TRet>(System.Func<int, TRet> okCase, System.Func<TRet> notFoundCase)
{
Dusharp.ExceptionUtils.ThrowIfNull(okCase, "okCase");
Dusharp.ExceptionUtils.ThrowIfNull(notFoundCase, "notFoundCase");
{
var unionCase = this as OkCase;
if (!object.ReferenceEquals(unionCase, null)) { return okCase(unionCase.i); }
}
{
var unionCase = this as NotFoundCase;
if (!object.ReferenceEquals(unionCase, null)) { return notFoundCase(); }
}
Dusharp.ExceptionUtils.ThrowUnionInInvalidState();
return default!;
}
public void Match<TState>(TState state, System.Action<TState, int> okCase, System.Action<TState> notFoundCase)
{
Dusharp.ExceptionUtils.ThrowIfNull(okCase, "okCase");
Dusharp.ExceptionUtils.ThrowIfNull(notFoundCase, "notFoundCase");
{
var unionCase = this as OkCase;
if (!object.ReferenceEquals(unionCase, null)) { okCase(state, unionCase.i); return; }
}
{
var unionCase = this as NotFoundCase;
if (!object.ReferenceEquals(unionCase, null)) { notFoundCase(state); return; }
}
Dusharp.ExceptionUtils.ThrowUnionInInvalidState();
}
public TRet Match<TState, TRet>(TState state, System.Func<TState, int, TRet> okCase, System.Func<TState, TRet> notFoundCase)
{
Dusharp.ExceptionUtils.ThrowIfNull(okCase, "okCase");
Dusharp.ExceptionUtils.ThrowIfNull(notFoundCase, "notFoundCase");
{
var unionCase = this as OkCase;
if (!object.ReferenceEquals(unionCase, null)) { return okCase(state, unionCase.i); }
}
{
var unionCase = this as NotFoundCase;
if (!object.ReferenceEquals(unionCase, null)) { return notFoundCase(state); }
}
Dusharp.ExceptionUtils.ThrowUnionInInvalidState();
return default!;
}
public virtual bool Equals(ResultSave? other) { return object.ReferenceEquals(this, other); }
public override bool Equals(object? other) { return object.ReferenceEquals(this, other); }
public override int GetHashCode() { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); }
public static bool operator ==(ResultSave? left, ResultSave? right)
{
return !object.ReferenceEquals(left, null) ? left.Equals(right) : object.ReferenceEquals(left, right);
}
public static bool operator !=(ResultSave? left, ResultSave? right)
{
return !object.ReferenceEquals(left, null) ? !left.Equals(right) : !object.ReferenceEquals(left, right);
}
private sealed class OkCase : ResultSave
{
public readonly int i;
public OkCase(int i)
{
this.i = i;
}
public override string ToString()
{
return $"Ok {{ i = {i} }}";
}
public override bool Equals(ResultSave? other)
{
if (object.ReferenceEquals(this, other)) return true;
var otherCasted = other as OkCase;
if (object.ReferenceEquals(otherCasted, null)) return false;
return StructuralEquals(otherCasted);
}
public override bool Equals(object? other)
{
if (object.ReferenceEquals(this, other)) return true;
var otherCasted = other as OkCase;
if (object.ReferenceEquals(otherCasted, null)) return false;
return StructuralEquals(otherCasted);
}
public override int GetHashCode()
{
unchecked { return System.Collections.Generic.EqualityComparer<int>.Default.GetHashCode(i!) * -1521134295 + "Ok".GetHashCode(); }
}
private bool StructuralEquals(OkCase other)
{
return System.Collections.Generic.EqualityComparer<int>.Default.Equals(i, other.i);
}
}
public static partial ResultSave Ok(int i)
{
return new OkCase(i);
}
private sealed class NotFoundCase : ResultSave
{
public static readonly NotFoundCase Instance = new NotFoundCase();
public NotFoundCase()
{
}
public override string ToString()
{
return "NotFound";
}
}
public static partial ResultSave NotFound()
{
return NotFoundCase.Instance;
}
}
}
Usefull
Download Example (.NET C# )
Share Dusharp
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Dusharp