Hans Bezemer <
the.beez.speaks@gmail.com> wrote:
No really, I'm not kidding. When done properly Forth actually changes
the way you work. Fundamentally. I explained the sensation at the end of
"Why Choose Forth". I've been able to tackle things I would never have
been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
I do not look at videos (mostly because they are extremally wasteful
way of transmiting concepts, with words once can do this faster).
So I will comment mostly on what you wrote.
Like I always wanted to do a real programming language - no matter how
primitive. Now I've done at least a dozen - and that particular trick
seems to get easier by the day.
I am not sure what you mean "do a real programming language".
I have written compilers. The ones where I did all the work
I consider to be toys. But I am pretty confident that if
I wanted I could extend them to a practical language. I also
work on real compilers, but here majority of work was done by
other people and I only worked on parts. Still, while in
a single compiler "my" part (or parts) are minority, they
together cover all stages of practical compiler.
I did not write a serious interpreter or even a part of it
but I looked at code in several interpreters and I think that
I understand subject well enough to write on if needed.
And IMHO a lot can be traced back to the very simple principles Forth is
based upon - like a stack. Or the triad "Execute-Number-Error". Or the
dictionary. But also the lessons from ThinkForth.
Traditional way to implement Forth is just one way. It is
relatively simple, so this may be attractive. But I would
say not the simplest one: bytecode interpreters are less
clever, so in a sense simpler (at cost of slower execution).
Compilers generating native code can be simple too, and
one can argue that they also need less cleverness than Forth
(but probably more object code).
You'll also find it in my C work. There are a lot more "small functions"
than in your average C program. It works for me like an "inner API". Not
to mention uBasic/4tH - There are plenty of "one-liners" in my
uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be
maintained by submitting to the very philosophy Forth was built upon. I
feel like if I would give in to locals, I'd be back to being an average
C programmer.
>
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
My philosophy for developing programs is "follow the problem".
That is we a problem to solve (task to do). We need to
understand it, introduce some data structures and specify
needed computation. This is mostly independent from programming
language. When problem is not well understood we need
to do some research. In this experiments may help a lot
and having interactive programming language in useful
(so this is plus of Forth compared to C). Once we have
data structures and know what computation is needed we
need to encode (represent) this in choosen language.
I would say that large scale structure of the program
will be mostly independent of programming language.
There will be differences at small scale, as different
languages have different idioms. "Builtin" features of
language or "standard" libraries may do significant
part of work. Effort of coding may vary widely,
depending how much is supported by the language and
surroundig ecosystem and how much must be newly
coded. Also, debugging features of programming
system affect speed of coding.
Frankly, I do not see how missing language features
can improve design. I mean, there are people who
try to use fancy features when thay are not needed.
But large scale structure of a program should not be
affected by this. And at smaller scale with some
experience it is not hard to avoid unneeded features.
I would say that there are natural way to approach
given problem and usually best program is one that
follows natural way. Now, if problem naturally needs
several interdependent attributes we need to represnt
them in some way. If dependence is naturaly in stack
way, than stack is a good fit. If dependence is not
naturaly in a stack way, using stack may be possible
after some reorganisation. But may experience is
that if a given structure does not naturally appear
after some research, than reorganisation is not
very likely to lead to such structure. And even if
one mananges to tweak program to such structure, it
is not clear if it is a gain. Anyway, there is substantial
number of problem where stack is unlikely to work in
natural way. So how to represnt attributes? If they
are needed only inside a single function, than natural
way is using local variables. One can use globals, but
for variables that are not needed outside a function
this in unnatural. One can use stack juggling, this
works, but IMO is unnatural. One can collect attributes
in a single structure dynamically allocated at
function entry and freed at exit. This works, but
again is unnatural and needs extra code.
Of course, sometimes other solutions are possible. Maybe
instead of separate variables one can recompute attributes
from something more basic. Maybe some group of attributes
is needed in several functions, then keeping them as part
of single structure is natural. But assuming that you
write program in natural way, you would choose alternative
what it is natural and choose locals only when thay
are a good fit.
You have some point about length of functions. While
pretty small functions using locals are possible, I
have a few longer functions where main reason for keeping
code in one function is because various parts need access
to the same local variables. But I doubt that eliminating
locals and splitting such functions leads to better code:
we get a cluster of function which depend via common
attibutes. This dependence is there regardless of having
single bigger function or several smaller ones (and
regardless how one represents attributes). But with a
single function dependence is explict, and for me easier
to manage.
Avoiding dependence helps, but above I mean unavoidable
dependence. And in fact, I find locals useful to avoid
false dependencies (where a buch of functons look like
they depend on something but in fact they do not).
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
The only thing I can say is, "it works for me". And when I sometimes
view the works of others - especially when resorting to a C style - I
feel like it could work for you as well.
Nine times out of ten one doesn't need the amount of locals which are
applied. One doesn't need a 16 line word - at least not when you
actually want to maintain the darn thing. One could tackle the problem
much more elegant.
My policy is that variable should be a single logical thing.
Which means that frequently I have more variables than
"strictly necessary". That is I do not reuse variable for
different purpose even if that would be possible. IMO
saving here are compiler job, and in case when compiler is
not doing this savings are not worth extra effort (and
IMO worse program structure). Not that in reasonable program
we are talking here about something like say 100 words
or maybe 1000 words which may be significant on a small
embedded system (but compilers for such system are reasonably
good at reusing variables), but is irrelevant for bigger systems.
-- Waldek Hebisch