> In your first example you don’t need the else clause and wouldn’t you clear up a lot of the invariants by checking leaf value (with ‘not a leaf’ appropriately represented) rather than whether there is a left child.
I'm not sure what you mean by "appropriately represented". What's an appropriate representation of "not present" if a leaf is allowed to be any int?
> Or even representing them with a function call isLeaf.
Let's see how it looks with an isLeaf() function:
struct BinaryTree {
leaf_value: int,
left_child: BinaryTree,
right_child: BinaryTree
}
function sum_leaves(tree: BinaryTree) -> int {
if tree.is_leaf() {
return tree.leaf_value;
else {
return sum_leaves(tree.left_child) + sum_leaves(tree.right_child);
}
}
It still has an "else" clause, and it's still full of invariants. Not sure how this is supposed to be much better?
> I don’t think you really make the case well here that it’d be superior rather than just personal preference.
Increased type safety is not personal preference! Here's the list of errors that are easy to make in the first example, and literally impossible in the second:
- accessing `leaf_value` when it's not set
- accessing `left_child` when it's nil
- accessing `right_child` when it's nil
- setting exactly one of `left_child`, `right_child` to nil
- setting `leaf_value` when `left_child` or `right_child` is nil
(You might imagine that the "setting" mistakes are possible in the second example too. If Go merely gained pattern matching, this would be the case. Most languages that were born with pattern matching, though, don't have default/uninitialized values for everything, and so don't let you make those mistakes. I.e., you cannot construct a BinaryTree in Rust without choosing a value for the leaf or for the two branches when you do.)