TaggedUnionVB.Generator 1.0.1

There is a newer version of this package available.
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
                    
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="TaggedUnionVB.Generator" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TaggedUnionVB.Generator" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="TaggedUnionVB.Generator" />
                    
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 TaggedUnionVB.Generator --version 1.0.1
                    
#r "nuget: TaggedUnionVB.Generator, 1.0.1"
                    
#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 TaggedUnionVB.Generator@1.0.1
                    
#: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=TaggedUnionVB.Generator&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=TaggedUnionVB.Generator&version=1.0.1
                    
Install as a Cake Tool

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

  1. Add the source generator as a NuGet package into your VB.NET project:
dotnet add package TaggedUnionVB.Generator
  1. Create .union files in your project, and set "Build Action" to "Additional Files":
<ItemGroup>
  <AdditionalFiles Include="**/*.union" />
</ItemGroup>
  1. The source generator will automatically create the corresponding VB.NET classes.
  2. 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 type
  • Public/Friend - Accessibility modifiers (optional, defaults to Public)
  • Of - Generic type parameters
  • ' or REM - 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., Vector2 from System.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

  • .union files should be included in your project as "Additional Files"
  • Generated files are created with _TaggedUnion.g.vb suffix
  • All unions from a single .union file 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:

  1. Clone or download the repository:
git clone https://github.com/Pac-Dessert1436/TaggedUnionVB.Generator.git
  1. Build the project using Visual Studio or .NET CLI:
dotnet build
  1. 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.

There are no supported framework assets in this package.

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.

Version Downloads Last Updated
1.1.1 96 5/3/2026
1.1.0 102 5/3/2026 1.1.0 is deprecated because it has critical bugs.
1.0.1 93 5/2/2026
1.0.0 102 5/2/2026 1.0.0 is deprecated because it has critical bugs.