Dear reader, try to figure out what this code does. This code isn't from an actual language but a typical typed language.
What is printed by print(result)
?
Most people would say, 5
is printed, which is a reasonable answer for people coming from object-orientated programming languages like Javascript or Java.
They would think of Bar
as an object whose value was initialized as 1
but then updated to 2
. Therefore when add
is called, we get 2 + 3 ⇒ 5
.
But what if I told you there is also another reasonable answer?
The other reasonable answer is 4
. This is for people who think of Bar
as a struct
. A struct looks like an object but is more like a value.
In systems programming languages like Zig or C, Bar
is a struct whose value was initialized as 1
. Since bar
is a value, when it is passed into Foo
, it is copied. Therefore, bar.value = 2
only changes the value for bar
but not the copy in foo.bar
.
When add
is called, foo.bar.value
is 1
; thus, result
is 1 + 3 ⇒ 4
.
It may seem like the trick was to know if Bar
was an object or struct, but I would argue the semantic trap was being ambiguous on the calling convention into Foo
. Was bar
pass by value (aka copied) or pass by reference?
This shift in thinking between object versus struct causes each camp to feel disgusted with the other. Each side doesn't understand how the other can write programs that make sense.
We should avoid this kind of thinking. We are all software developers and should pick the best tool for the job. Sometimes the best tool is one you don't know how to use yet.
Code sample of call by reference in Typescript and call by value in Zig.
Do you want to practise seeing the forest for the trees? You're in luck, Battlefy is hiring.