2025-07-28 02:55:26 is there a forth that uses 0{ to create locals initialized to 0? seems like something ive seen but cant find it 2025-07-28 17:44:02 I haven't seen that many Forth implementations that implement locals tbh 2025-07-28 17:51:44 it makes a huge difference in readability 2025-07-28 17:52:23 another thing that came up today was using unused registers to buffer locals 2025-07-28 17:52:58 since you can look at the assembly listing to see if there are any registers that arent used by the primitives in that section of the program and figure out if theyre free to use 2025-07-28 17:54:48 such register usage quickly becomes unmanageable as soon as you have words that use non-primitive words 2025-07-28 17:55:25 that's fine 2025-07-28 17:55:42 you can document register clobbering in your word documentation, but you soon realize that you end up having to push/pop your clobbered registers all the time 2025-07-28 17:56:26 I would just use a script to look at the assembly or compiler output to see which registers are used 2025-07-28 17:56:48 then the Forth could find sections where registers are free and it makes sense to use them 2025-07-28 17:58:03 sure, but what I mean is that because it's fun to use those registers, you're going to use them in a lot of places. As soon as you go 2 or 3 levels above the primitive level, your pretty listing will tell you "all registers are used below!" 2025-07-28 17:58:22 no reason to analyze what's used below 2025-07-28 17:58:46 I wouldnt just assumed theyre used and stop using them when a non-primitive is called 2025-07-28 17:58:52 *would 2025-07-28 17:59:36 oh, alright then. It limits their uses to fewer use cases, but make the system more manageable indeed 2025-07-28 18:41:08 vdupras: what I usually do when I'm programming in assembly is use a standard calling convention in which some registers are never clobbered, such as PC and SP, and other registers are always assumed to be clobbered by a call 2025-07-28 18:42:31 you need a non-call-preserved register for return values at least, and on RISCs the call instruction clobbers the return-address register 2025-07-28 18:46:07 so a subroutine that needs a few temporaries or locals can use the non-call-preserved registers for them, while a subroutine that needs more locals, or locals that survive calling another subroutine, can save and restore call-preserved registers for them 2025-07-28 18:47:44 it's pretty easy because you never have to analyze which subroutines are using which registers 2025-07-28 18:48:46 I'm unconvinced that "locals" in the sense of stack-allocated variables make a huge difference in readability 2025-07-28 18:49:41 without "locals": 0 value foo 0 value bar : glorp ... foo ... bar ... ; : quux to foo to bar ... foo ... bar ... glorp ... ; 2025-07-28 18:50:08 with "locals": : glorp { foo bar } ... foo ... bar ; : quux { foo bar } ... foo ... bar ... bar foo glorp ... ; 2025-07-28 18:51:28 here I'm assuming that { } are initializing the "locals" from the stack 2025-07-28 18:51:51 I mean there is a difference but it isn't clear which one is more readable 2025-07-28 18:52:21 and when you're interactively poking at the system, the first one lets you type foo . and the second one doesn't 2025-07-28 18:53:26 stack-allocated variables do help a lot if you're using recursion 2025-07-28 20:53:06 interesting perspective 2025-07-28 20:53:28 here's an example where I think locals helps a lot: https://pastebin.com/8vweDMAR 2025-07-28 20:55:07 I see how using regular variables is about the same. I think local variables are helpful when the alternative is stack juggling 2025-07-28 20:56:12 theyre also a lot faster on my system since a small part of memory where the locals are stored is much faster than main memory but that's not the case for most systems I think 2025-07-28 21:01:25 yeah, stack juggling sucks 2025-07-28 21:03:51 I think that your particular nqueens example would probably be about the same amount of code and about as readable with regular variables, but surely you are correct that on some systems it would be slower. especially if your Forth compiler can allocate registers to the variables 2025-07-28 21:05:09 OTOH using regular variables might enable you to factor it so that you can test the pieces incrementally without needing a single-step debugger 2025-07-28 21:05:46 another case that someone had mentioned was where the total size of their program's local variables was a substantial fraction of their RAM, so being able to reuse the same stack space for the locals of non-overlapping subroutines was important 2025-07-28 21:08:16 I was working on a 'new forth' design and scoped variables was one of my core principles 2025-07-28 21:08:41 You can still RPN with scoped variables, but there is something elegant about the more point-free stack juggling 2025-07-28 21:09:05 But it's just tedious, you will get more done with scoped vars, I can't lie 2025-07-28 21:09:36 But then you think ... why even bother with Forth and RPN. If you've got locals, or optimisation, at what point do you just throw in the towel and use something like tcc instead 2025-07-28 21:09:56 so, in standard Forth, variable scope extends from the variable definition until the next declaration of a variable with the same name, or until you switch wordlists/vocabularies 2025-07-28 21:10:06 Similar kind of complexity and much easier to write; realistic use as an 'interpreter' or on an embedded system 2025-07-28 21:10:24 I guess part of what I'm talking about is automatic storage duration, rather than just purely 'scope' 2025-07-28 21:10:30 right, extent rather than scope 2025-07-28 21:10:51 it's easy to get them confused because in a lot of languages they coincide 2025-07-28 21:10:53 I said scope to make it easier for people to understand 2025-07-28 21:11:04 that seems counterproductive :-) 2025-07-28 21:11:21 It's counterproductive around language lawyers 2025-07-28 21:11:48 it's counterproductive when we're trying to discuss the advantages and disadvantages of different possible ways of designing a FOrth 2025-07-28 21:12:17 because a lot of the disadvantages of C-style global variables are specifically disadvantages of global scope, not of indefinite extent 2025-07-28 21:12:58 I think you can get Forth-style hiding in C with the right linker 2025-07-28 21:13:06 somewhat 2025-07-28 21:13:30 Not standard anymore but most C isn't 'standard' in intent nor reality 2025-07-28 21:13:30 that gives you most of vocabularies 2025-07-28 21:13:44 well, you can define variables file-static in C 2025-07-28 21:13:48 which is standard 2025-07-28 21:14:10 I do agree it's a disadvantage I have to apply paranoia prefixes to my globals in C 2025-07-28 21:14:21 but you can't do int x; foo() { ... x ... } int x; bar() { ... x ... } in C 2025-07-28 21:14:28 that I'm aware of 2025-07-28 21:14:44 unless foo and bar are in different files 2025-07-28 21:14:46 right 2025-07-28 21:14:50 exactly 2025-07-28 21:15:07 Part of the reason for this is probably that Forth is an environment and C is a programming language 2025-07-28 21:15:16 yes 2025-07-28 21:15:42 and in an interactive environment it's useful to be able to interrogate variables to see what a word did to them 2025-07-28 21:15:47 Forth is influenced here by the need to keep running and build potentially multiple unrelated programs over each other; C you just put them in different small units 2025-07-28 21:16:04 hmm, that's true too 2025-07-28 21:16:30 although to a significant extent you can get that in Forth just by forgetting the program you're not using at the moment (or markering it) 2025-07-28 21:16:44 The differences here are interesting to me, the philosophy of running small processes, forking etc, in conventional systems C code vs how a Forth application would be written 2025-07-28 21:16:55 If it really required something like pipes / threads 2025-07-28 21:17:29 some of this is sort of a local-optimum equilibrium. I watched an interview with Carmack the other day where he was talking about IDEs and debuggers 2025-07-28 21:17:49 and sort of laughing at how Unix programmers disdain debuggers 2025-07-28 21:18:28 because, like, they could save a huge amount of time if they didn't have to wait for their program to recompile to see the effect of their changes, right? 2025-07-28 21:18:31 heh, funny how many people roll their eyes at gdb because they think you mean typing stuff into a terminal to check variable values 2025-07-28 21:18:46 you can do that but have these people seen `layout reg` and so on? 2025-07-28 21:18:56 I really like gdb 2025-07-28 21:19:01 and it hit me that he's programming in C++ and he's been programming in C++ for like 30 years 2025-07-28 21:19:20 and in C++ not recompiling really does save a huge amount of time 2025-07-28 21:19:33 Interesting 2025-07-28 21:19:37 whereas in C recompilation is almost instant 2025-07-28 21:19:51 I'm spoilt mostly writing C where even on my slow laptop building is basically a couple seconds 2025-07-28 21:19:57 yeah 2025-07-28 21:20:03 With optimisations turned on 2025-07-28 21:20:04 and a lot of time in Unix we're writing things whose user interface is the command line 2025-07-28 21:20:20 so the other thing the debugger saves you, getting to the point in your program where things break, is also less important 2025-07-28 21:20:38 On Linux usually if something breaks and I care enough I have a shot at fixing it, on Windows there's basically no chance 2025-07-28 21:20:56 And I see stuff break on both all the time, so I can't be bothered with Windows 2025-07-28 21:21:10 I avoid having to fix stuff but sometimes my hand is forced 2025-07-28 21:21:37 like, I don't want to claim that this is some kind of great feat of software engineering, but here's my test suite for a Quicksort function I wrote in assembly the other week: http://canonical.org/~kragen/sw/dev3/quicksort_tests 2025-07-28 21:21:43 it's a shell script 2025-07-28 21:22:35 it takes 200 milliseconds to run 2025-07-28 21:23:17 Very nice 2025-07-28 21:24:08 so you have these two equilibria, one where compilation is painfully slow, and starting a program is slow, and your program has a lot of state, so it's really a huge advantage to be able to peek around inside it and tweak the data with a debugger 2025-07-28 21:24:08 I mostly use bourne shell to drive building and testing now 2025-07-28 21:25:22 and another where the debugger doesn't work all that well and breaks sometimes and is kind of primitive (sorry MrMobius) but compilation and restarting are instant 2025-07-28 21:25:44 I wonder if big game engines would be able to save/reload module state and use dynamic loading/closing of libraries to avoid some of that overhead 2025-07-28 21:26:08 maybe; Muratori did that in one of his projects 2025-07-28 21:26:14 Wellons blogged about the technique 2025-07-28 21:27:55 https://nullprogram.com/blog/2014/12/23/ 2025-07-28 21:27:57 im guilty of printf debugging in C until it gets to a certain point when I switch to a logging system I set up 2025-07-28 21:28:13 when the going gets tough, the tough use printf 2025-07-28 21:28:29 each log type can be turned on and off independently and is well formatted and all logging can be compiled out with a flag 2025-07-28 21:28:51 yeah 2025-07-28 21:29:10 often I use grep after the fact for turning log types on and off 2025-07-28 21:29:25 https://news.ycombinator.com/item?id=42607087 is a discussion with Kenton Varda about printf debugging 2025-07-28 21:31:37 structured logging can help significantly 2025-07-28 21:34:07 Tim Bray had a fantastic blog post about printf debugging that I can't find 2025-07-28 21:39:40 printf definitely important tool, can see how things evolve and go wrong, harder to do with a 'debugger' 2025-07-28 21:41:49 I usually go straight into the debugger though, I've used printf (well actually an 'array' instead usually for embedded issues, but essentially the same thing) to debug some nasty problems 2025-07-28 21:46:31 it's possible to script a debugger to add debug printfs at runtime 2025-07-28 21:46:51 which is sometimes a useful technique, especially if recompiling takes a long time or recreating program state does 2025-07-28 21:47:58 the embedded array printf technique is explored in some depth in https://yosefk.com/blog/delayed-printf-for-real-time-logging.html 2025-07-28 21:48:02 The nasty issues I run into tend to be very sensitive to timing, so a local array is my equivalent 2025-07-28 21:48:14 yeah, Yossi's post is about the timing thing 2025-07-28 21:48:21 and a debugger is kind of anathema there 2025-07-28 21:48:55 And the debugger, everything it does other than a breakpoint has massive impact on timing, so I'll write some code specifically to hook breakpoints and then that's all it can do, really 2025-07-28 21:49:36 yeah 2025-07-28 21:50:01 even in non-embedded environments the timing hit can be pretty significant 2025-07-28 21:50:36 dtrace apparently can insert low-cost "probes" for that kind of thing? But I've never learned to use it 2025-07-28 21:50:41 The embedded debugger issue seems to be more to do with the communication between the MCU and the debug server, it's really bad 2025-07-28 21:51:04 On desktop the debugger's never caused me trouble, although I know it would in some situations 2025-07-28 21:53:07 I enjoy using things like conditional breakpoints on desktop 2025-07-28 22:05:22 yeah, typically the debug protocol is something like JTAG: bit serial with a low clock rate 2025-07-28 22:05:47 you can make your conditional breakpoint print out the printf message and then continue 2025-07-28 22:06:03 for the last 10 years or so you can script GDB in Python for that