Hacker News new | past | comments | ask | show | jobs | submit login

Many Forth developers do not like using named parameters AKA local variables. That said, they certainly exist, so if that is your only objection to Forth then there are easy ways to solve it.

See, for example,

http://zedcode.blogspot.com/2011/02/forth-parameter-stack-na...




I've since discovered that the latest Forth standard, Forth-2012, includes an optional set of Forth words to support local variables.

Gory details here (warning, spec-ese): http://forth-standard.org/standard/locals

I don't know how widely these are supported in Forth implementations.


All variables in Forth are local variables. They're just, traditionally, statically allocated, like a static variable in a C program. Their scope extends from their declaration to wherever the wordlist they're defined in stops being visible, or until another variable of the same name is defined, which means their scope usually does not extend to the end of your program.

I think the spec's choice to term stack-allocated variables "locals" (for what we call "automatic" variables in C) is a bad choice, because it confuses the issue further. When we refer to "local" or "global" variables, normally we are referring to scope, which is a question of over what part of a program a name maps to a particular location (or, in functional languages, sometimes a value). But what we're talking about here is extent or lifetime: during what period of time is a particular location allocated. These are clearly not unrelated concerns, since a name cannot usefully map to an unallocated location, and the same static occurrence of a name can map to multiple different locations when that name is bound on different binding contours, which cannot happen when the name is bound in the global scope; but these two concerns are also not identical, and I think it is important to distinguish them.

In Scheme, for example, the scoping rules are the same block-scoping rules we're familiar with from Pascal and C; but the extent of all Scheme values is unlimited — they can continue to exist until the end of the program, regardless of which subroutines have returned. This is also true in the ML family (OCaml, Haskell) and the abundant crop of modern Scheme-descended languages, including JS, Perl5, Python, Lua, and Ruby, but not in C++ or Rust. (Most, though, don't follow Scheme's mind-bending lead in having first-class continuations with unlimited extent, though Ruby does.)

So, what the Forth-2012 locals word set provides is names with dynamic or automatic extent: they refer to values that exist during a particular activation of a particular function. It also provides local scoping for names for them, but this is a much less significant change.


We can certainly quibble about the names given in a standard, but "local" is the term the Forth specification uses, so that's what I'm going with.

> All variables in Forth are local variables.

That's true in some sense (that is, all variables must be in scope to refer to them). However, I think that statement is a little misleading.

Traditional Forth variables often do have scope that extends to the end of your program (and possibly beyond). E.g., if your program says:

VARIABLE FOO

Then that's essentially a global variable in other languages; any function can read/fetch from foo (FOO @) or write/store to FOO (new-value FOO !). Indeed, you can read/write them after your program has completed.

Forth locals do not have this extent; they disappear when the function encompassing them returns. I note that Forth locals are allowed to be stored on the return stack, so that is one way to implement them. Thus, Forth locals are basically like "auto" in other languages like C.


> E.g., if your program says:

> VARIABLE FOO

> Then that's essentially a global variable in other languages; any function can read/fetch from foo (FOO @) or write/store to FOO (new-value FOO !)

This is the point where I disagree. Only functions statically within the scope of FOO --- that is, where FOO is visible when they are defined --- can read or write (or call) FOO, unless you pass them a pointer to it. If you write the following function in GCC, we have a similar situation with the variable m:

    int (*adder(int n))(int)
    {
        static int m;
        int rv(int p) { return m + p; }
        m = n;
        return rv;
    }
m is visible to the inner function rv, because it is within its scope. It retains its value after adder returns, because it is statically allocated and thus has unlimited extent. (Static allocation is the only way to get unlimited extent in C or Forth, since they don't have garbage collection; woe unto those who attempt to use this adder function in almost any real situation!) But this does not mean that m is a global variable. On the contrary, it is a local variable lexically scoped to the body of adder. A similar situation obtains for traditional Forth variables, it's just that the scopes are not block-structured. But neither are they usually the rest of your program.

Several of the problems of global variables in other programming languages are absent or less serious with Forth variables. As I mentioned in the other thread, non-preemptive multitasking avoids the problem of another concurrent thread overwriting the variable except at well-defined (if implicit) yield points, and because each thread has its own dictionary, ordinary variables like FOO can be thread-local. The fact that Forth variables must be declared, and a Forth variable's lexical scope is suspended when another variable of the same name is declared, means that you don't have the collision problem you have in C, where if two libraries both define a global variable called "errnum", it causes a link error, or under some circumstances, both libraries use the same variable. (The wordlist system, providing separate namespaces for separate libraries, also helps with this.) Because recursion doesn't happen implicitly in Forth, and you have a stack you can explicitly save values on, statically-allocated variables don't prevent reentrancy in Forth as they do in old versions of FORTRAN, or as they do if you use them in C.

So, yes, Forth "locals" are basically like auto variables in other languages like C, in that they are allocated in the same way. But, in C local variables are implicitly auto, and in Forth they aren't; and in C making a variable non-auto is usually a bad idea, and in Forth it isn't.


Forth-2012's local variable support includes support for {: ... :}. A "|" within this separates parameters (which are loaded from passed parameters) from local variables. If I understand the spec correctly, it looks like you could do this:

    : MY-NAME {: var1 var2 var3 :} var1 var2 + var3 * ;
Which is exactly what was desired.


Right, but even without the Forth-2012 LOCALS word set, you can and often should say something like

    0 value var1  0 value var2  0 value var3
    : my-name to var3 to var2 to var1  var1 var2 + var3 * ;
Maybe in this case you would just say

    : my-name >r + r> * ;
or

    : my-name -rot + * ;
but what I mean is that when you find that it's not obvious what's on the stacks, you can always just stick things in variables until it becomes obvious, just like you'd do in C or Python.

A dangerous temptation with Forth is to use the stacks whenever you'd use local variables in other programming languages. A little bit of that can be good, but it only takes four or five variables before it becomes completely unreadable. It never becomes impossible, because with >r and r> you already have a Turing machine, and with dup and swap you can arbitrarily splice its tape, but it very quickly becomes a Turing tarpit. Instead, if you don't have {: :}, and maybe even if you do, your default replacement for local variables in other languages should be traditional Forth variables, even though they're statically allocated. The reasons to studiously avoid global variables in other programming languages are a lot weaker or inapplicable for traditional Forth variables.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: