4 ms·
> ...then a() needs to crash when first called, because b() hasn't been declared yet. Why would it “need to” crash? If it is easier to implement to not crash a
by krisoft 3y ago
> ...then a() needs to crash when first called, because b() hasn't been declared yet.
Why would it “need to” crash? If it is easier to implement to not crash and the developer intention is clear why would you define your new programing language such that it “needs to crash” in this situation?
It is as if you go to your garden to pick tomatoes, but you trip over a rake you intentionally put in your way and then as you lay on the ground hurting you conclude that it is impossible to pick tomatoes. Picking the tomatoes in this case is making a compiled programing language which has a main file instead of a main function. And the rake you trip over is the intentional decision to make the program crash in this situation. Just don’t put the rake there (choose to not crash in this situation) and then you can pick the tomatoes (have a compiled language with a main file instead if a main function).
There might be more complicated reasons why it is neccesary to have a main function, but this example does not really demonstrate it for me.
- ufo 3y agoOne corner case is that b() might use variables that are initialized further down in the file. Some languages, such as Javascript, lift function definitions to the top so you can call functions defined below you, but it's harder to do the same for variables. In a compiled language it should be possible to detect this at compile time, but it's fiddly and more complicated than having a main function.
- noduerme 3y agoFrom Mozilla's docs: >> JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, classes, or imports to the top of their scope, prior to execution of the code. >> Hoisting is not a term normatively defined in the ECMAScript specification. The spec does define a group of declarations as HoistableDeclaration, but this only includes function, function*, async function, and async function* declarations. Hoisting is often considered a feature of var declarations as well, although in a different way. In colloquial terms, any of the following behaviors may be regarded as hoisting: >> Being able to use a variable's value in its scope before the line it is declared. ("Value hoisting") >> Being able to reference a variable in its scope before the line it is declared, without throwing a ReferenceError, but the value is always undefined. ("Declaration hoisting") >> The declaration of the variable causes behavior changes in its scope before the line in which it is declared. >> The side effects of a declaration are produced before evaluating the rest of the code that contains it. So basically all the shit we take for granted when not writing C/C++. What is curious to me is whether the main reasons for not attempting this in a C++ compiler in 2023, or else moving the language spec in this (unofficial) direction, is to produce maximal performance-optimization, or if it's mostly a cultural thing at this point. It does have the benefit of turning away hordes of javascript kiddies from the gates, but, the relative value of the performance edge to any optimization is getting weaker as compute becomes cheaper.
- deleted 3y ago[deleted]
- ufo 3y agoThe genie got somewhat out of the bottle with constructors: C++ will call constructors to initialize static variables. In theory we could use this mechanism to allow top-level statements. However, these static initializers are so full of footguns that perhaps it's best we don't :P
- ekidd 3y ago> Why would it “need to” crash? If it is easier to implement to not crash and the developer intention is clear why would you define your new programing language such that it “needs to crash” in this situation? Well, let me come up with another example: def a(x=SIZE): ... a() let SIZE = 16*1024 When we call a(), we need the value of SIZE to provide the a default value for x. But SIZE isn't computed yet. We could try to "hoist" SIZE, but normally that just means we have: let SIZE = undefined def a(x=SIZE): ... a() SIZE = 16*1024 And sure, you could invent a rule to "fix" this case, too. (It depends on how you implement default arguments efficiently.) But next week, you'll encounter another headache, and another. I've literally been through this a couple of times working on LISP compilers. "Executional" semantics are common in custom Lisp dialects, and it's a huge amount of work to get them right. The price you end up paying is lower program performance and higher compiler complexity. Oh, and importantly, you normally wind up with slower load times. A compiled program is "ready to run", and can be loaded efficiently using mmap() and maybe some linking. But a program where you "execute" the top level needs to run all those top-level definitions on each load. So then you're like, "I know! I'll write a heap dumper/undumper", aka "unexec". Which will fix this problem but cause 5 more. And so it goes. This is one of those ideas that seems clever but leads to bitter regrets, at least in high performance languages.
- krisoft 3y agoThank you for the example. > And sure, you could invent a rule to "fix" this case, too. I would just do the simple thing. If X is not already defined when it is used in the function signature I would just give a compile time error. Easy to implement for the compiler author, easy for the developer to understand and rectify. > But next week, you'll encounter another headache, and another. I believe you. I guess it is one of those things one has to see for themselves to really appreciate.