More about Unions in Vala

In a previous post I speculated about adding tagged unions to Vala. Let’s do that again.

In order to support the C usecase, Vala would need to include 2 types of tagged unions, a legacy one, and an idiomatic one. The legacy should look like the C version of a tagged union:

struct tagged {
    short tag;
    union variants {
        string text;
        int num;
    }
} tagged;

This is needed to bind the C libs out there. The type of the tag should be something that can be easily compared IMO (numeric datatypes, C enums, and string). The binding would then look like this:

[CCode (cname = "tagged", union_tag_field = "tag" union_tag_type = "short" union_field = "variants")]
union Tagged {
    [CCode (cname = "text", union_tag_id = "1")]
    TEXT(string),
    [CCode (cname = "num", union_tag_id = "2")]
    NUM(int)
}

The idiomatic ones, however, would actually look like a Rust struct, so if we declare:

public union OrderStatus {
	ACCEPTED,
	CANCELLED {string reason, string cancelled_by},
	REJECTED {string reason},
	COMPLETED {DateTime completed_at},
	ON_HOLD {string reason, Datetime until}
}

We should get:

enum OrderStatusTag {
	ACCEPTED,
	CANCELLED,
	REJECTED,
	COMPLETED,
	ON_HOLD
} OrderStatusTag

union order_status {
    struct accepted {OrderStatusTag tag};
    struct cancelled {OrderStatusTag tag; string reason, string cancelled_by};
    struct rejected {OrderStatusTag tag; string reason};
    struct completed {OrderStatusTag tag; GDateTime completed_at};
    struct on_hold {OrderStatusTag tag; string reason; GDatetime until};
} OrderStatus;

Fun things to support: GVariant variant types (unboxing, serialization, etc.), GValue, JSON representations.

Advertisements

Converting Array fields from PostgreSQL in jOOQ

There’s currently a “bug” (not really a bug, but it works for me in MySQL) that prevents jOOQ from reusing a forcedType when it appears as an array instead of a single value. I have a workaround for this: a forcedType for the array field, and binding it by field name. To facilitate the creation of an Array Converter, just subclass this and pass the right converter in the super() call:

Unions in Vala

I’ve started to use Kotlin professionally, and keeping an eye on Rust. Both offer a lot of niceties that I wish we could adapt for Vala, but there’s one that keeps popping up my mind everytime: pattern matching.

The simplest pattern matching we have is C unions, and a lot of c-libs use them. Unfortunately, the current handling of unions in Vala is a disgrace, and there’s no alternative for it. But I believe we can import some syntax from both Kotlin and Rust. Here is my proposal of how should unions work in Vala:

//Opening a bracket defines an "anonymous struct"
public union OrderStatus {
	ACCEPTED,
	CANCELLED {string reason, string cancelled_by},
	REJECTED {string reason},
	COMPLETED {DateTime completed_at}
	ON_HOLD {string reason, Datetime until}
}

match (order.status) {
	ACCEPTED -> info("Cool!");
	CANCELLED -> debug(@"Not okay, it was cancelled because of $(it.reason) by $(it.cancelled_by)");
	REJECTED as that -> info (@"Rejected: $that.reason");
	default -> error("What is this?? There's no implicit \"it\" here because it's a catch-all!")
}

public union NestedUnion {
	SIMPLE,
	union COMPLEX {
		SOFT,
		HARD{string reason}
	}
}

//The additional field belongs to the wrapping struct

public union ComplexUnion {
	FIRST,
	SECOND,
	THIRD {string reason};
	//parent field, children cannot have a field with the same name
	uint32 timestamp;
}

//Maybe this is not a good idea

public union VeryComplex {
	FIRST {
		override void run() {
			log("Uy!")
		}
	},
	SECOND {
		override void run() {
			log("Ouch!");
		}
	},
	THIRD {
		override void run() {
			log("Ay!");
		}
		override void do() {
			debug ("Yay!");
		}
	}
	
	//They are all required to implement it!
	abstract void run();
	
	//Optionally overriden
	virtual void do() {
		
	}
	//Can't touch this
	public void execute() {
	}
}

//In this case, they reuse existing datatypes

public union ExternalUnion {
	STRING(string),
	NUMBER(uint64),
	THINGY(GLib.Object)
}

public void method () {
	var order = new Order(OrderStatus.ON_HOLD("reason", DateTime.now()));
	var other_order = new Order(OrderStatus.CANCELLED(cancelled_by = "desiderantes", reason = "who knows!")); 
	
	var nested = NestedUnion.COMPLEX.HARD(reason = "no reason at all");
	//'match' can return a value, but all branches should return the same type
	//this 'match' in particular is exhaustive, so no default needed, but if you return a value from 'match', you have to either
	//cover all cases or have a default branch
	
	NestedUnion another_nested = get_from_network();
	var reason = match (another_nested) {
		SIMPLE -> "Just because";
		COMPLEX -> match (it) {
			SOFT -> "Really easy";
			//if not renamed, then you'll lose access to the it from outer context, as it'll be shadowed
			HARD as that -> that.reason;
		}
	}
	
	//This errors
	var complex = ComplexUnion(123456789);
	var complex = ComplexUnion();
	var complex = ComplexUnion.FIRST();

	//This should work
	var complex = ComplexUnion.THIRD(123456789, "I can");
	var complex = ComplexUnion.THIRD(reason = "Just because", timestamp = 321654987);
	
	
	match (complex) {
		//properties from the parent are only accessible from the parent reference, no implicit parent var
		FIRST -> debug(@"$(complex.timestamp)");
		SECOND -> debug ("Oops");
		THIRD -> debug @("$(complex.timestamp) by $(it.reason)");
	}
	
	var external = ExternalUnion.STRING("this string is required");
}

The internal structure (C-wise) of my proposed tagged union is not anything new, it has been done a lot before in C land (here is an explanation from the Rust viewpoint)

Tip: Emitting the notify signal in Vala

I needed a way to emit the notify signal of one of my objects from another place, and Vala didn’t show me a straightforward way to do it. If you need it for some reason, here’s a code snippet showing you how:

using GLib;

public class TestClass : GLib.Object {
	public string test1 {get;set;default = "test1";}
	//Ths one won't emit on assignment
	[CCode (notify = false)]
	public string test2 {get;set;default = "test2";}

	public static void main (string[] args) {
		var test = new TestClass ();
		test.notify["test1"].connect (() => GLib.print ("test1 notification\n"));
		test.notify["test2"].connect (() => GLib.print ("test2 notification\n"));
		test.test1 = "Ahoy";
		test.test2 = "Újale";
		test.test2 = "Ajúa";
		ParamSpec pspec = ((ObjectClass) typeof (TestClass).class_ref ()).find_property ("test2");
		GLib.print ("First try, will not work %s\n", pspec.name);
		test.notify(pspec);
		GLib.print ("second one, this is how it works");
		test.notify["test2"] (pspec);
		return;
	}
}

Replay Edits

Being in the zone is amazing: your productivity skyrockets and the code just flows. Then somebody micromanages talks to you, the boss starts to scream discuss things on the phone, or it’s lunch time and this company won’t give back any minute you waste before starting to eat. Since this hapenned to me at least once, I’ve been looking for a simple way to get back my productivity when I resume my job. I’ve found that the best way for me is just doing insane amounts of Ctrl+Z and Ctrl+Shift+Z/Ctrl+Y to see what I did and where did I did it. But this is a tiresome activity, the kind that I expect to be automated. So why not add a simple option in your text editor, a nice Replay Edits that’ll just play in real time edits from last saved state to current state? I want this, and if you know something like this, PLEASE tell me, I’m all ears.

Abandoned Vala Projects

There was a time when Vala was really popular, and a plethora of Vala apps spawned. A lot of them are dead right now, so since i don’t have time to revive any of them, i’ll publish this list of interesting projects in case someone is interested in bringing back one of them:

On Vala and Composite Templates

If you want to use the GTK+ composite templates in Vala, please remember : you have to use a GResource. The syntax is not exactly hard, here’s an example:

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gtk/Example">
<file compressed="true">window.ui</file>
</gresource>
</gresources>

Which is pretty simple. Let’s say you called that file project.gresource.xml, so to include it, you do (at compile time)

valac --target-glib=2.38 --gresources project.gresource.xml --pkg your_package1 --pkg you_package2 ...

Of course, it can’t be that simple, so first you have to _compile_ that xml file (crazy, isn’t it?), doing

glib-compile-resources project.gresource.xml

and then include the generated C file. As this won’t scale, and you probably aren’t compiling that way, the documentation suggest to include a make rule for this. So, our make rule should look like this:

project-gresource.c: project.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies project.gresource.xml)

and we can include the generated C file in our valac call:

valac --target-glib=2.38 --gresources project.gresource.xml --pkg your_package1 --pkg you_package2 file1.vala file2.vala project-gresource.c ...

Please don’t forget to read the announcement here for more info.