Atomicity of tuple assigment
February 2, 2019 Leave a comment
Tuples in C# have been present since version 4.0 as a library without any language support. C# 7.0 finally introduces proper language integration of tuples. For example, the syntax to create a tuple is simply:
var pair = (1,2);
Deconstructing is also supported:
var (x,y) = pair;
Combining these two features lets us use tuple assignment to swap variables:
(a,b) = (b,a);
Which is almost equivalent to, but more compact and more explicit than, the following:
var temp = a; a = b; b = temp;
This behaviour of tuple is akin to atomicity, indeed the left-hand side variables, are only assigned to once all the right-hand side ones have been evaluated. In pseudo syntax of my transactional model we would have:
transaction { a = b; b = a; }
where a
and b
are both semi-mutable variables, hence changes would only be visible after the atomic block. Or in standard C# syntax:
var tempA = a; var tempB = b; a = tempB; b = tempaA;
The tuple assignment syntax to swap two variables simplifies some standard algorithms such as the partitioning in the quick sort algorithm:
public int Partition(int[] v, int low, int high) { var p = v[high]; var i = low-1; for (int k = low; k < high; k++) { if (v[k] < p) { i++; if (i != k) { (v[i], v[k]) = (v[k], v[i]); } } } i++; (v[i], v[high]) = (v[high], v[i]); return i ; }
where the big advantage is that there is no need to use a temporary value or to worry about the order of the various node references are assigned. The downside is readability which decreases as the number of variables increases.
Other algorithms which manipulate linked list can also benefit from this syntax, for example the partitioning of a linked list can be written as:
private ListNode partition(ListNode node, int value) {
if (node == null) return null;
ListNode head = node;
while (node.next != null) {
if (node.next.value < value) {
(node.next, node.next.next, head) = (node.next.next, head, node.next);
} else {
node = node.next;
}
}
return head;
}
where the big advantage is that there is no need to use a temporary value or to worry about the order of the various node references are assigned. The downside is readability which decreases as the number of variables increases.
The atomicity of this tuple assignment in C# is much stronger than, for example, in Python. You might have noticed that in the tuple assignment we first assign a new value to node.next and then to node.next.next. This will give a wrong result in Python, as the lhs are assigned in turn. In C#, the address of the destination variable is evaluated and stored before any assignment is performed.
In terms of atomicity of this assignment operation in case of exceptions, exceptions thrown while evaluating the right-hand side will result in none of the left-hand side value being assigned to. However, if any setter in the left-hand side throws, we can end-up with partially assigned values:
(c.r,c.i) = (1,2);
if the setter of c.i throws an exception, only c.r will have been assigned to, leaving c.i unassigned.
Albeit this assignment is only partially atomic, I find it useful in algorithms where is various variables or properties are swapped around.