02:55:26
##forth
<MrMobius>
is there a forth that uses 0{ to create locals initialized to 0? seems like something ive seen but cant find it
17:44:02
##forth
<hello-operator>
I haven't seen that many Forth implementations that implement locals tbh
17:51:44
##forth
<MrMobius>
it makes a huge difference in readability
17:52:23
##forth
<MrMobius>
another thing that came up today was using unused registers to buffer locals
17:52:58
##forth
<MrMobius>
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
17:54:48
##forth
<vdupras>
such register usage quickly becomes unmanageable as soon as you have words that use non-primitive words
17:55:25
##forth
<MrMobius>
that's fine
17:55:42
##forth
<vdupras>
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
17:56:26
##forth
<MrMobius>
I would just use a script to look at the assembly or compiler output to see which registers are used
17:56:48
##forth
<MrMobius>
then the Forth could find sections where registers are free and it makes sense to use them
17:58:03
##forth
<vdupras>
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!"
17:58:22
##forth
<MrMobius>
no reason to analyze what's used below
17:58:46
##forth
<MrMobius>
I wouldnt just assumed theyre used and stop using them when a non-primitive is called
17:58:52
##forth
<MrMobius>
*would
17:59:36
##forth
<vdupras>
oh, alright then. It limits their uses to fewer use cases, but make the system more manageable indeed
18:41:08
##forth
<xentrac>
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
18:42:31
##forth
<xentrac>
you need a non-call-preserved register for return values at least, and on RISCs the call instruction clobbers the return-address register
18:46:07
##forth
<xentrac>
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
18:47:44
##forth
<xentrac>
it's pretty easy because you never have to analyze which subroutines are using which registers
18:48:46
##forth
<xentrac>
I'm unconvinced that "locals" in the sense of stack-allocated variables make a huge difference in readability
18:49:41
##forth
<xentrac>
without "locals": 0 value foo 0 value bar : glorp ... foo ... bar ... ; : quux to foo to bar ... foo ... bar ... glorp ... ;
18:50:08
##forth
<xentrac>
with "locals": : glorp { foo bar } ... foo ... bar ; : quux { foo bar } ... foo ... bar ... bar foo glorp ... ;
18:51:28
##forth
<xentrac>
here I'm assuming that { } are initializing the "locals" from the stack
18:51:51
##forth
<xentrac>
I mean there is a difference but it isn't clear which one is more readable
18:52:21
##forth
<xentrac>
and when you're interactively poking at the system, the first one lets you type foo . and the second one doesn't
18:53:26
##forth
<xentrac>
stack-allocated variables do help a lot if you're using recursion
20:53:06
##forth
<MrMobius>
interesting perspective
20:55:07
##forth
<MrMobius>
I see how using regular variables is about the same. I think local variables are helpful when the alternative is stack juggling
20:56:12
##forth
<MrMobius>
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
21:01:25
##forth
<xentrac>
yeah, stack juggling sucks
21:03:51
##forth
<xentrac>
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
21:05:09
##forth
<xentrac>
OTOH using regular variables might enable you to factor it so that you can test the pieces incrementally without needing a single-step debugger
21:05:46
##forth
<xentrac>
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
21:08:16
##forth
<veltas>
I was working on a 'new forth' design and scoped variables was one of my core principles
21:08:41
##forth
<veltas>
You can still RPN with scoped variables, but there is something elegant about the more point-free stack juggling
21:09:05
##forth
<veltas>
But it's just tedious, you will get more done with scoped vars, I can't lie
21:09:36
##forth
<veltas>
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
21:09:56
##forth
<xentrac>
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
21:10:06
##forth
<veltas>
Similar kind of complexity and much easier to write; realistic use as an 'interpreter' or on an embedded system
21:10:24
##forth
<veltas>
I guess part of what I'm talking about is automatic storage duration, rather than just purely 'scope'
21:10:30
##forth
<xentrac>
right, extent rather than scope
21:10:51
##forth
<xentrac>
it's easy to get them confused because in a lot of languages they coincide
21:10:53
##forth
<veltas>
I said scope to make it easier for people to understand
21:11:04
##forth
<xentrac>
that seems counterproductive :-)
21:11:21
##forth
<veltas>
It's counterproductive around language lawyers
21:11:48
##forth
<xentrac>
it's counterproductive when we're trying to discuss the advantages and disadvantages of different possible ways of designing a FOrth
21:12:17
##forth
<xentrac>
because a lot of the disadvantages of C-style global variables are specifically disadvantages of global scope, not of indefinite extent
21:12:58
##forth
<veltas>
I think you can get Forth-style hiding in C with the right linker
21:13:06
##forth
<xentrac>
somewhat
21:13:30
##forth
<veltas>
Not standard anymore but most C isn't 'standard' in intent nor reality
21:13:30
##forth
<xentrac>
that gives you most of vocabularies
21:13:44
##forth
<xentrac>
well, you can define variables file-static in C
21:13:48
##forth
<xentrac>
which is standard
21:14:10
##forth
<veltas>
I do agree it's a disadvantage I have to apply paranoia prefixes to my globals in C
21:14:21
##forth
<xentrac>
but you can't do int x; foo() { ... x ... } int x; bar() { ... x ... } in C
21:14:28
##forth
<xentrac>
that I'm aware of
21:14:44
##forth
<MrMobius>
unless foo and bar are in different files
21:14:46
##forth
<xentrac>
right
21:14:50
##forth
<xentrac>
exactly
21:15:07
##forth
<veltas>
Part of the reason for this is probably that Forth is an environment and C is a programming language
21:15:16
##forth
<xentrac>
yes
21:15:42
##forth
<xentrac>
and in an interactive environment it's useful to be able to interrogate variables to see what a word did to them
21:15:47
##forth
<veltas>
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
21:16:04
##forth
<xentrac>
hmm, that's true too
21:16:30
##forth
<xentrac>
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)
21:16:44
##forth
<veltas>
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
21:16:55
##forth
<veltas>
If it really required something like pipes / threads
21:17:29
##forth
<xentrac>
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
21:17:49
##forth
<xentrac>
and sort of laughing at how Unix programmers disdain debuggers
21:18:28
##forth
<xentrac>
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?
21:18:31
##forth
<MrMobius>
heh, funny how many people roll their eyes at gdb because they think you mean typing stuff into a terminal to check variable values
21:18:46
##forth
<MrMobius>
you can do that but have these people seen `layout reg` and so on?
21:18:56
##forth
<veltas>
I really like gdb
21:19:01
##forth
<xentrac>
and it hit me that he's programming in C++ and he's been programming in C++ for like 30 years
21:19:20
##forth
<xentrac>
and in C++ not recompiling really does save a huge amount of time
21:19:33
##forth
<veltas>
Interesting
21:19:37
##forth
<xentrac>
whereas in C recompilation is almost instant
21:19:51
##forth
<veltas>
I'm spoilt mostly writing C where even on my slow laptop building is basically a couple seconds
21:19:57
##forth
<xentrac>
yeah
21:20:03
##forth
<veltas>
With optimisations turned on
21:20:04
##forth
<xentrac>
and a lot of time in Unix we're writing things whose user interface is the command line
21:20:20
##forth
<xentrac>
so the other thing the debugger saves you, getting to the point in your program where things break, is also less important
21:20:38
##forth
<veltas>
On Linux usually if something breaks and I care enough I have a shot at fixing it, on Windows there's basically no chance
21:20:56
##forth
<veltas>
And I see stuff break on both all the time, so I can't be bothered with Windows
21:21:10
##forth
<veltas>
I avoid having to fix stuff but sometimes my hand is forced
21:21:43
##forth
<xentrac>
it's a shell script
21:22:35
##forth
<xentrac>
it takes 200 milliseconds to run
21:23:17
##forth
<veltas>
Very nice
21:24:08
##forth
<xentrac>
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
21:24:08
##forth
<veltas>
I mostly use bourne shell to drive building and testing now
21:25:22
##forth
<xentrac>
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
21:25:44
##forth
<veltas>
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
21:26:08
##forth
<xentrac>
maybe; Muratori did that in one of his projects
21:26:14
##forth
<xentrac>
Wellons blogged about the technique
21:27:57
##forth
<MrMobius>
im guilty of printf debugging in C until it gets to a certain point when I switch to a logging system I set up
21:28:13
##forth
<xentrac>
when the going gets tough, the tough use printf
21:28:29
##forth
<MrMobius>
each log type can be turned on and off independently and is well formatted and all logging can be compiled out with a flag
21:28:51
##forth
<xentrac>
yeah
21:29:10
##forth
<xentrac>
often I use grep after the fact for turning log types on and off
21:31:37
##forth
<xentrac>
structured logging can help significantly
21:34:07
##forth
<xentrac>
Tim Bray had a fantastic blog post about printf debugging that I can't find
21:39:40
##forth
<veltas>
printf definitely important tool, can see how things evolve and go wrong, harder to do with a 'debugger'
21:41:49
##forth
<veltas>
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
21:46:31
##forth
<xentrac>
it's possible to script a debugger to add debug printfs at runtime
21:46:51
##forth
<xentrac>
which is sometimes a useful technique, especially if recompiling takes a long time or recreating program state does
21:48:02
##forth
<veltas>
The nasty issues I run into tend to be very sensitive to timing, so a local array is my equivalent
21:48:14
##forth
<xentrac>
yeah, Yossi's post is about the timing thing
21:48:21
##forth
<xentrac>
and a debugger is kind of anathema there
21:48:55
##forth
<veltas>
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
21:49:36
##forth
<xentrac>
yeah
21:50:01
##forth
<xentrac>
even in non-embedded environments the timing hit can be pretty significant
21:50:36
##forth
<xentrac>
dtrace apparently can insert low-cost "probes" for that kind of thing? But I've never learned to use it
21:50:41
##forth
<veltas>
The embedded debugger issue seems to be more to do with the communication between the MCU and the debug server, it's really bad
21:51:04
##forth
<veltas>
On desktop the debugger's never caused me trouble, although I know it would in some situations
21:53:07
##forth
<veltas>
I enjoy using things like conditional breakpoints on desktop
22:05:22
##forth
<xentrac>
yeah, typically the debug protocol is something like JTAG: bit serial with a low clock rate
22:05:47
##forth
<xentrac>
you can make your conditional breakpoint print out the printf message and then continue
22:06:03
##forth
<xentrac>
for the last 10 years or so you can script GDB in Python for that