TaggedUnionVB.Generator
1.0.1
See the version list below for details.
dotnet add package TaggedUnionVB.Generator --version 1.0.1
NuGet\Install-Package TaggedUnionVB.Generator -Version 1.0.1
<PackageReference Include="TaggedUnionVB.Generator" Version="1.0.1" />
<PackageVersion Include="TaggedUnionVB.Generator" Version="1.0.1" />
<PackageReference Include="TaggedUnionVB.Generator" />
paket add TaggedUnionVB.Generator --version 1.0.1
#r "nuget: TaggedUnionVB.Generator, 1.0.1"
#:package TaggedUnionVB.Generator@1.0.1
#addin nuget:?package=TaggedUnionVB.Generator&version=1.0.1
#tool nuget:?package=TaggedUnionVB.Generator&version=1.0.1
TaggedUnionVB.Generator
A source generator that creates tagged union types for VB.NET, providing type-safe pattern matching capabilities similar to discriminated unions in functional languages.
v1.0.1 Stable Release: Fixed implicit conversion operators and multiple generic type parameters.Only this version is literally fully-tested. Users should still define custom types in their VB.NET projects for each union case.
⚠️ v1.0.0 is broken: Version 1.0.0 has critical issues with implicit conversions and generic type handling. Please upgrade to v1.0.1 or later.
Description
This source generator simplifies the creation of tagged union types (also known as "discriminated unions") in VB.NET, by parsing .union files and generating the corresponding VB.NET classes with pattern matching capabilities.
The design follows Microsoft's planned C# 15 union keyword syntax, where users define their own case classes/structs and the discriminated union simply references them.
Installation
- Add the source generator as a NuGet package into your VB.NET project:
dotnet add package TaggedUnionVB.Generator
- Create
.unionfiles in your project, and set "Build Action" to "Additional Files":
<ItemGroup>
<AdditionalFiles Include="**/*.union" />
</ItemGroup>
- The source generator will automatically create the corresponding VB.NET classes.
- If you want to build this generator from source, see the Building from Source section.
Usage
Creating Union Definitions
Create files with the .union extension in your project. These files contain (and only contain) simple union definitions, together with namespace imports:
Imports System.Numerics
' Valid union with existing, imported types
Public Union VectorUnion(Vector2, Vector3, Vector4)
' Users should define a Just(Of T) class and a Nothing class
Public Union Maybe(Of T)(Just(Of T), [Nothing])
' Users should define an Ok(Of T) class and an Error(Of E) class
Friend Union Result(Of T, E)(Ok(Of T), [Error](Of E))
⚠️ Important: If the compiler throws a "type doesn't exist" error, you must define the union case types in your VB.NET project first (see Example section for examples).
Syntax Rules
Keywords
Imports- Import required namespaces (optional)Union- Declare a union typePublic/Friend- Accessibility modifiers (optional, defaults to Public)Of- Generic type parameters'orREM- Line comments (lines starting with these are ignored)
Union Declaration Format
[Public|Friend] Union Name[(Of TypeParam1, TypeParam2, ...)](Case1, Case2(Of T), Case3)
This syntax is for .union files, and mainly supports:
- primitive types like
Integer,String,Double - existing types from imported namespaces (e.g.,
Vector2fromSystem.Numerics) - user-defined classes or structs
Use square brackets around reserved keywords, like [Nothing].
Generated Code Features
For each union definition, the generator creates:
- Base abstract class with pattern matching methods
- Wrapper classes for each union variant that hold existing type instances
- Pattern matching methods (
IsXxx(),AsXxx()) - Implicit conversions from existing types to union
- XML documentation comments
Example
Input: User-Defined Types and .union Files
First, define your case types in VB.NET:
Public Class Just(Of T)
Public ReadOnly Property Value As T
Public Sub New(value As T)
Me.Value = value
End Sub
End Class
Public Class [Nothing]
End Class
Then in a .union file:
Public Union Maybe(Of T)(Just(Of T), [Nothing])
Output: Generated VB.NET Classes
' <auto-generated>
' This code was generated by `TaggedUnionVB.Generator`
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
Option Explicit On
Option Strict On
''' <summary>
''' <para>Tagged Union: Maybe(Of T)</para>
''' <para>From declaration: <c>Public Union Maybe(Of T)(Just(Of T), [Nothing])</c></para>
''' </summary>
Public MustInherit Class Maybe(Of T)
Private Sub New()
End Sub
''' <summary>
''' Returns True if this Maybe is a Just
''' </summary>
Public Function IsJust() As Boolean
Return TypeOf Me Is Just_Case
End Function
''' <summary>
''' Returns the Just(Of T) value if this is a Just, otherwise throws.
''' </summary>
Public Function AsJust() As Just(Of T)
Dim wrapper As Just_Case = TryCast(Me, Just_Case)
If wrapper Is Nothing Then
Throw New InvalidOperationException("Maybe is not a Just.")
End If
Return wrapper.Value
End Function
''' <summary>
''' Returns True if this Maybe is a Nothing
''' </summary>
Public Function IsNothing() As Boolean
Return TypeOf Me Is Nothing_Case
End Function
''' <summary>
''' Returns the [Nothing] value if this is a Nothing, otherwise throws.
''' </summary>
Public Function AsNothing() As [Nothing]
Dim wrapper As Nothing_Case = TryCast(Me, Nothing_Case)
If wrapper Is Nothing Then
Throw New InvalidOperationException("Maybe is not a Nothing.")
End If
Return wrapper.Value
End Function
''' <summary>
''' Wrapper for Just(Of T) in Maybe(Of T)
''' </summary>
Public NotInheritable Class Just_Case
Inherits Maybe(Of T)
''' <summary>
''' The underlying Just(Of T) value
''' </summary>
Public ReadOnly Property Value As Just(Of T)
''' <summary>
''' Creates a new Just_Case
''' </summary>
''' <param name="value">The Just(Of T) value</param>
Public Sub New(value As Just(Of T))
Me.Value = value
End Sub
''' <summary>
''' Implicit conversion from Just(Of T) to Maybe(Of T)
''' </summary>
Public Shared Widening Operator CType(value As Just(Of T)) As Maybe(Of T)
Return New Just_Case(value)
End Operator
End Class
''' <summary>
''' Wrapper for [Nothing] in Maybe(Of T)
''' </summary>
Public NotInheritable Class Nothing_Case
Inherits Maybe(Of T)
''' <summary>
''' The underlying [Nothing] value
''' </summary>
Public ReadOnly Property Value As [Nothing]
''' <summary>
''' Creates a new Nothing_Case
''' </summary>
''' <param name="value">The [Nothing] value</param>
Public Sub New(value As [Nothing])
Me.Value = value
End Sub
''' <summary>
''' Implicit conversion from [Nothing] to Maybe(Of T)
''' </summary>
Public Shared Widening Operator CType(value As [Nothing]) As Maybe(Of T)
Return New Nothing_Case(value)
End Operator
End Class
End Class
Usage in Code (Actually Works in 1.0.1)
' Create union values using implicit conversion
Dim someValue As Maybe(Of Integer) = New Just(Of Integer)(42)
Dim noValue As Maybe(Of Integer) = New [Nothing]()
' Pattern matching
If someValue.IsJust() Then
Dim just = someValue.AsJust()
Console.WriteLine($"Just value: {just.Value}")
End If
If noValue.IsNothing() Then
Console.WriteLine("Nothing value")
End If
Advanced Features
Generic Unions
Unions can be generic with multiple type parameters:
Public Union Maybe(Of T)(Just(Of T), [Nothing])
Public Union Result(Of T, E)(Ok(Of T), [Error](Of E))
Result Union Example
For a Result(Of T, E) union:
' Input .union file
Friend Union Result(Of T, E)(Ok(Of T), [Error](Of E))
The generator produces a union with proper generic case handling:
Public MustInherit Class Result(Of T, E)
' ...
Public Function IsOk() As Boolean
Return TypeOf Me Is Ok_Case
End Function
Public Function AsOk() As Ok(Of T)
Dim wrapper As Ok_Case = TryCast(Me, Ok_Case)
' ...
Return wrapper.Value
End Function
Public Function IsError() As Boolean
Return TypeOf Me Is Error_Case
End Function
Public Function AsError() As [Error](Of E)
Dim wrapper As Error_Case = TryCast(Me, Error_Case)
' ...
Return wrapper.Value
End Function
NotInheritable Class Ok_Case
Inherits Result(Of T, E)
Public ReadOnly Property Value As Ok(Of T)
' ...
End Class
NotInheritable Class Error_Case
Inherits Result(Of T, E)
Public ReadOnly Property Value As [Error](Of E)
' ...
End Class
End Class
Existing Type Wrappers
The generator creates wrappers for existing types with implicit conversions:
Imports System.Numerics
Public Union Shape(Circle(Of Single), Rectangle(Of Single, Single), [Nothing])
Pattern Matching
The generated classes provide type-safe pattern matching through IsXxx() and AsXxx() methods.
File Structure
.unionfiles should be included in your project as "Additional Files"- Generated files are created with
_TaggedUnion.g.vbsuffix - All unions from a single
.unionfile are generated into one output file
Error Handling
The generator includes error handling that will silently skip invalid union definitions. Ensure your syntax follows the specification.
Limitations
- Union definitions must be on a single line
- Case names cannot contain spaces
- Generic parameters must be simple identifiers
- No support for nested unions or recursive definitions
- Reserved keywords used as case names must be wrapped in square brackets (e.g.,
[Nothing])
Building from Source
To build the source generator from its source code, follow these steps:
- Clone or download the repository:
git clone https://github.com/Pac-Dessert1436/TaggedUnionVB.Generator.git
- Build the project using Visual Studio or .NET CLI:
dotnet build
- Reference the built assembly in your target VB.NET project:
dotnet add path/to/YourProject.vbproj reference TaggedUnionVB.Generator.vbproj
License
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.