I was reading through this article on polymorphism in zig when I stumbled across this:
Below, you will see me use a pattern like this:
const SomeVar = struct { fn SomeFunction(SomeArgs) void { DoSomething(). } }.SomeFunction
This is a workaround for Zigs lack of inline function declarations. You should think of the above as
const SomeVar = fn(SomeArgs) void { DoSomething(); };
Proposal #1717 will address this eventually, but in the meantime this is required.
So, why not just use the latter implementation?
After sifting through the proposal on github, it seems that the community is very fond of functions as expressions. The proposal was accepted and all seemed to be well. Until it was rejected.
This was Andrew’s reasoning. It seemingly got met with disapproval.
Allow me to address the two goals of this proposal:
Provide syntactic consistency among all statements which bind something to an identifier
This proposal does an excellent job at accomplishing this goal, and is the reason I originally accepted it. However, I will now make an argument that there is a good reason for there to not be syntax consistency between function declarations and other declarations.
Ultimately, Zig code will output to an object file format, an executable binary, or an intermediate format such as LLVM IR or C code that is ultimately destined for such a place. In those places, functions have symbol names. These symbol names show up in stack traces, performance measurements, debugging tools, and various other things. In other words, functions are not unnamed. This is different from constants and types, which may exist ephemerally and be unnamed.
So, I think the syntax inconsistency appropriately models reality, making Zig a better abstraction over the artifacts that it produces.
Provide syntactic foundation for a few features: functions-in-functions (#229), passing anonymous funtions as arguments (#1048)
I have rejected both of these proposals. In Zig, using functions as lambdas is generally discouraged. It interferes with shadowing of locals, and introduces more function pointer chasing into the Function Call Graph of the compiler. Avoiding function pointers in the FCG is good for all Ahead Of Time compiled programming languages, but it is particularly important to zig for async functions and for computing stack upper bound usage for avoiding stack overflow. In particular, on embedded devices, it can be valuable to have no function pointer chasing whatsoever, allowing the stack upper bound to be statically computed by the compiler. Since one of the main goals of Zig is code reusability, it is important to encourage zig programmers to generally avoid virtual function calls. Not having anonymous function body expressions is one way to sprinkle a little bit of friction in an important place.
Finally, I personally despise the functional programming style that uses lambdas everywhere. I find it very difficult to read and maintain code that makes heavy use of inversion of control flow. By not accepting this proposal, Zig will continue to encourage programmers to stick to an imperative programming style, using for loops and iterators.
So I reiterate, why aren’t functions expressions? As far as I can tell, from what the github comments show, there’s no reason not do so — is there?