Skip to main content

Posts

Showing posts from 2021

Generating types with build-in invariant preservation using C# source generators (2 / x) : Composition to form complex types

In the last post about "Generating types with build-in invariant preservation using C# source generators" I mentioned composing instances of Validated<T> in a smart way to form complex types that in turn also perverse their invariants. This post will go more into detail on how to do that. Validated<T> captures the essence of wetter or not an instance of a type T is in a valid state or not.  Validated<T> has 2 subtypes, namely Valid<T> and Invalid<T>. You will never guess what they represent ... Ok, you did. Valid<T> indeed means that whatever the state an instance of T is in, it is in a valid state. Invalid<T>, ... well I think you get it. So how do we guarantee the instance of T is in a valid or invalid state? Well, by validating the input that leads to a state change. When programming in a functional style, which we are doing here, we don't want to let the state of an instance of T change at all after it has been created. So th

Generating types with build-in invariant preservation using C# source generators (1 / x) : Simple types

In an earlier post, I described a way for generating value types. In this post, I want to show how to take this a step further. Usually, one does not only want an alias but enforce an invariant using the type system at the same time.  So first let's recap what we mean by an invariant. Plainly put, an invariant is a condition that stays true no matter what happens. For example, let's suppose we have a value in our system that represents a quantity that must always be greater than zero. That is an invariant. Invariants pop up all over the place when modeling a domain. We want to enforce these invariants because we want to guard the integrity of our domain model. One very convenient way of guarding the integrity of values within the domain is by capturing invariants directly into the type system. Once you successfully create an instance of a type guarding the invariant, you never have to check its validity again. In addition to that, the validation logic is captured in 1 place, wh

Running Microsoft Playwright in an Azure Function using C#

When you have tried to run MS Playwright using C# in the context of an Azure Function, you probably have run into this message: The driver it is referring to resides in the .playwright folder that is copied to the build output folder. Now, the output folder structure of an Azure Function project is different from most projects in the sense that there is an extra nested bin folder where the drivers should actually be copied.  The build target that the Playwright team uses, at the time of writing (version 1.15.4), always copies the folder containing the driver to the root of the output folder. So the fix here is to add an extra build target to your project file, the corrects for the extra nested folder:   <Target Name="FixPlaywrightCopyAfterBuild" AfterTargets="Build">     <ItemGroup>       <_BuildCopyItems Include="$(OutDir).playwright\**" />     </ItemGroup>     <Message Text="[Fix] Copying files to the nested bin folder o

Type aliases using C# Source Generators and C# 10 generic attributes

When practicing domain-driven design, a reoccurring chore is the creation of value types. These are strongly typed representations of simple types like strings that have a specific meaning, like CustomerId or ProductCode. These could both be strings but we put preferably implement them as strongly typed variations so that we can't for instance mix up multiple string parameters while coding. In some languages, this is something that comes out of the box and is often referred to as type aliasing. C# does not support this. Although in C# you could give another name to a string type with a using statement, it still remains a string (the type does not change).  This task is so common and tedious that it makes it the perfect case for implementing a source generator. Creating a type alias should be as simple as adding an attribute indicating what type should be aliased. It should also work for all kinds of types, like class, record, struct, and record struct. The full source code can be f

Simple but effective use of C# Source Generators

Most C# source generator examples I have encountered included more advanced features like augmenting existing classes with generated code. There are much simpler scenarios where they are useful though. For instance, just avoiding typing repetitive code. For the full source code, consult my GitHub repo here . Imagine writing a number of overloads where only the function name changes. In this case, I want to create HTML tags as strings using a function. The example is a set of overloads for the anchor ('a') tag: public static Node a(params Node[] nodes) =>      element(nameof(a), Array.Empty<IAttribute>(), nodes); public static Node a(params IAttribute[] attributes) =>      element(nameof(a), attributes, Array.Empty<Node>()); public static Node a(IEnumerable<IAttribute> attributes, params Node[] children) =>      element(nameof(a), attributes, children); Now I want the have the same overloads for all HTML tags. That's a lot of repetition. To cre