Lerc
5 months ago
Having done a fair degree of programming in Wirthwhile languages, I think the only main design decision that I think was a mistake was the variables at the top.
I'm not sure of the value of seeing all of the variables used listed in one place, it has certainly led to me encountering a identifier scrolling up to determine the type then scrolling back down. When the variable is only used on a few consecutive lines it's just adding work to read and adding work to create. I daresay I have written harder to read code than I intended because I didn't want to go up and declare another variable for short term use. The temptation to inline the expression is always there, because you know what all the parts mean when you write it. It's only when you come back later that you get the regret.
It's possible it could be mitigated by defining something like (not sure if this is a valid grammar)
stmt_sequence = {decl_sequence}. stmt {";" stmt} [";"].
and bring in scoping on statement sequences. maybe call it stmt_block so that stmt_sequence can be a component that really is just a sequence of statements.munificent
5 months ago
> I'm not sure of the value of seeing all of the variables used listed in one place
It means the compiler knows how much memory the function's activation frame will take and the offset into that for every variable before it encounters any code in the function.
Basically, it makes it easier to write a single-pass compiler. That was important in the 70s but is less important these days.
cb321
5 months ago
A lot of people in this subthread are echoing this, but it's at most maybe very slightly easier. For example, TinyCC is a one-pass C compiler and yet C has sub-scopes int foo() { /.../ { int x; /.../ } }. The same could be said of Ken Thompson's C compiler which I believe was also one-pass.
Does Titania not have/intend to have lexically local subscopes? That would seem very un-Wirthian to me.
munificent
5 months ago
One difference here might be that most Pascals support local functions and C does not.
michaelcampbell
5 months ago
> Wirthwhile languages,
This made me smile; going to use it in the future.
doodpants
5 months ago
Though it would be pronounced vehrt-while.
michaelcampbell
5 months ago
Of course. It's more a visual pun than audible, but I liked it nonetheless.
Rochus
5 months ago
This design decision makes compiler implementation easier and especially enables single-pass compilation. Later Oberon versions at least supported more than one declaration section in arbitrary order, but still no in-place declarations.
troupo
5 months ago
Wirth was obsessed with the idea of creating the absolutely minimal useful language, and many of his languages' warts come from that.
Variables are at the top because:
- you immediately see them (so, perhaps, easier to reason about a function? I dunno)
- the compiler is significantly simplified (all of Wirths' languages compile superfast and, if I'm not mistaken, all are single-pass compilers)
However, I feel that Wirth was overly dogmatic on his approaches. And "variables must always be at the top" is one of those.
gingerBill
5 months ago
This has nothing to do with "dogma" and something simpler. It has nothing to do with "immediately see them".
Hint: This about this from a single pass compiler basis and how much memory needs to be reserved from the procedure's stack frame.
troupo
5 months ago
"This is nothing to do with something simpler" and "this is from a single pass compiler".
Are you sure you actually read my second bullet point?
If you read texts and papers by Wirth you'll see a single theme emerge: simplicity. Everything he didn't consider simple was thrown away and derided.
NuclearPM
5 months ago
Lua is single pass too and doesn’t have the same restrictions.
troupo
5 months ago
Wirth is also from a time when resources were incredibly constrained. So it was a combination of his ideas for what a PL should be: easy to understand, easy to learn, powerful enough to do everything he needed. And the compiler for the language also needed to be the same.
Unfortunately he got kinda stuck in that, and you can see it in all the Oberon flavors: he was clearly fighting against the limitations he imposed, but couldn't break out if them.
foldl2022
5 months ago
A nice side-effect of "variables at the top": you keep your functions short.
Turskarama
5 months ago
"Functions should always be short" is also one of those guidelines that people treat like a hard rule. There are occasions when a 100 line function is easier to read than 5 20 line functions, or god forbid 20 5 line functions.
Stop being overly dogmatic, it ALSO leads to worse code.
lenkite
5 months ago
There are occasions => There are only rare occasions.
mannschott
5 months ago
One would assume that, but in practice, the predominant style is not one of many short procedures. Instead it feels that there's a preference to just inline the code unless the resulting procedure will have more than one caller.
For example, search for "PROCEDURE Scan" here: https://people.inf.ethz.ch/wirth/ProjectOberon/Sources/Texts...
Control structures are deeply nested and this goes on for 64 (very dense) lines. The low line count but is an artifact of how Oberon is conventionally formatted. When reformatted to mimic the conventions of languages like C, Java or Python it works out to more than 120 lines.
When I program in Oberon (recreationally) I tend to follow this style even though I would extract the same code into a separate method were I writing in Java.
gingerBill
5 months ago
When I write the backend (this repo isn't even 24 hours old yet), you'll find out why variable declarations are at the top of a procedure. (Hint: it has something to do with the stack).
alexjplant
5 months ago
I wondered why my university made us use C90 for Systems Programming class (circa 2010) until I took Compilers. This quirk specifically stood out to me when considering code generation from an AST - it's a lot easier to simply allocate all required memory at the top of a stack frame when you have the variable declarations at the top of the function.
Lerc
5 months ago
I'm aware that it lets you do things in a single pass manner, but this is the instance where I think the cost for allowing that is too great.
I always thought there must be a better solution, like emitting the compiled function body first which just increments the offset whenever a space for a variable is required and emit the function entry after the function exit last, so you can set up the stack frame with full knowledge of it's use. Then the entry can jump to the body. Scoping to blocks would let you decrement the offset upon exiting the block which would result in less stack use which would almost always be more beneficial than the cost of the additional jump.
gingerBill
5 months ago
If you want single pass, then you have to do it on a per block basis at the most (e.g. C89).
But this is not meant to be a fully fledged language, it's meant to be a teaching tool. If you want a fully fledged language that allows for out of order declarations, try Odin!
Also, the syntax of Oberon/Pascal doesn't really allow for it in a nice way. It kind of looks really weird when you allow for it out of order.