Steps for creating a programming language:
- Think about who will want to use your new language and for what.
- Conjecture a unified mental model of how a programmer should think about your language. Make it fit how your users think and their expected use cases.
- Translate your mental model into machine code.
- Announce your new language to the world, making sure to report how users should think about your language.
Steps for creating a modern general-purpose programming language:
- Define your target audience as "everyone" and their expected use cases as "anything ever".
- Make a unified mental model of how everyone should approach doing anything ever. Model it after how everyone currently does everything.
- ???
- Be a large corporation. Make everyone use your language to interact with your products. Eventually, people will start using it for other tasks out of familiarity.
That was probably the fastest I have yet derailed a blog post. I want to talk about how to make a proper mental model.
Switch-statements
C# requires a control flow statement at the end of every case in a switch-statement. I'm sure they wished to add an implicit `break` after every (non-empty) case, but that would have shocked developers coming from C, where cases fall through by default. So they resigned themselves to making the user choose control-flow behavior explicitly, presumably reasoning that any default would surprise someone.
I know why C chose default fall-through and C# didn't.
In my oversimplified understanding, control flow constructs compile to jump-instructions--the equivalent of using `goto` everywhere. Switch-statements compile to jump tables, which figure out how far forward in the code to jump before you start executing again.
Notably, a jump table says nothing about where to jump after that. It doesn't know what a switch case is. So a `break` statement at the end of the switch case is another jump instruction--extra effort for the runtime. Fall-through is the default in C because it is the default for the implementation. C probably expects its programmers to know already basically what the assembler will do and to write programs based on that knowledge.
C# doesn't do that. C# would consider that an implementation detail. Why would you need to know assembly concepts to program in C#? In C#, fall-through requires the use of `goto`, the most damning marker of unidiomaticity that I can imagine. The creators of the C# language did not want people using switch-statements with fall-through. (Stephen Toub does, though, if I remember correctly.)
(Actually, switch-statements don't always compile to jump tables in C#. The probably do if you're switching on int (or char or bool), but they can also do pattern matching using the same syntax. In C#, the code you write may have a completely different implementation based on context.)
The point
Why does every modern language try to be opaque?
Richard Gabriel says that simplicity of implementation is at least as important as simplicity of interface. What sense does this make unless the user is supposed to understand the implementation? And Joel Spolsky argues that the user will need a correct mental model of the implementation anyway. These are big figures in software, worthy of our attention.
We all agree that programs should be simple. Is there any plausible measurement of software simplicity other than how easy it is for the computer to run? ("Quick for the programmer to write" is a separate question; a throwaway script may rely on brute force.)
To write good code, you must first have the right mental model. The right mental model is that which most correctly describes the problem. In programming, every problem is flipping some bits somewhere into the proper state. So should we not be thinking in those terms? Should we not use technologies that we are allowed to understand?
No comments:
Post a Comment