Hacker News new | ask | show | jobs
by bjarneh 1388 days ago
> Variables are not unique. You can declare multiple values for the same variable name using ":=" and append in the stack. If you want to edit the last declaration, just user simple "=" operator. To remove the last variable with specific name in stack, use "del varname"

Isn't this very confusing in almost all instances?

3 comments

Forth behaves the same way. The vocabulary is really a stack, and you can define words, redefine them, and then pop them back off to return to the old value.

It's really not useful for variables, but can be very useful for functions, and since they're pretty much the same thing, you get the behavior for free. Although I haven't touched Lisp, my understanding is that Forth and Lisp have some conceptual similarities, but Forth is written by an engineer and Lisp by a computer scientist. Limit you for your own safety? Not a thing in Forth.

Seems like an explicit/less confusing way to do variable shadowing, though imho that's not a feature worth implementing in a 400LOC language.
It being explicit/arbitrary makes it more confusing, not less. At least most languages with variable shadowing, there's a block of some sort that defines the scope. Here it'd just be anywhere that it can change meaning.
In block scoped languages, you cannot access variables outside of the scope. They disappear entirely when the scope terminates (exemplified by C and its ilk), or are captured in a closure (Lisp) possibly downward-funarg-only (Pascal) such that only the body of the closure can access them.

In this Kamby language, it looks like the bindings survive and are then accessible by name. When you're executing these [ ... ] blocks, a tree-shaped environment is built up which is then navigated with the :: operator, enabling it to simulate objects with fields.

The idea of there being a stack-like environment of variables which can survive the blocks in which they are bound is very useful in pattern matching and unification.

Under pattern matching, the successful path will have the cumulative variables arising from everything that has successfully matched, including some nested constructs. However, the search strategy will implicitly backtrack in searching for matches, and in backtracking, it will implicitly erase any variables accumulated in the discarded paths.

In the TXR pattern language, I invented some mechanisms for controlling the proliferation of variables. When a pattern function is used, only those variables that correspond to its arguments can emerge.

  $ txr -B -c '@(define fun (arg1 arg2))
  @(bind arg1 "a1")
  @(bind arg2 "a2")
  @(bind arg3 "a3")
  @(end)
  @(fun x y)'
Output:

  y="a2"
  x="a1"
How it works is that the unbound variables x and y are identified with the arg1 and arg2 variables. The pattern function executes, binding arg1, arg2 and arg3. Then a resolution step is done at return-time. Because arg1 was identified with unbound x, x now receives that binding. Similar for y. The variable arg3 disappears; that entire local environment of the function is discarded, and x and y are grafted onto the original environment that existed on entry into the function.

If we bind an argument, the parameter will be bound on entry into the function, and so then has to unify in the subsequent bind directive. It does if we pass the value "a1":

  txr -B -c '@(define fun (arg1 arg2))
  @(bind arg1 "a1")
  @(bind arg2 "a2")
  @(bind arg3 "a3")
  @(end)
  @(fun "a1" y)'
  y="a2"
fails if we pass some other value:

  txr -B -c '@(define fun (arg1 arg2))
  @(bind arg1 "a1")
  @(bind arg2 "a2")
  @(bind arg3 "a3")
  @(end)
  @(fun "foo" y)'
  false
The variable binding environment isn't reified as a value that can itself be bound as a variable and inspected; in any context, there is one environment which contains everything that was done in the chain of pattern matching leading up to that point; dead ends that were backtracked out of are gone.
> less confusing way to do variable shadowing

How is this less confusing? := vs = will either push a new variable on top of a stack of identically named variables, or change the content of the top element on that stack? Why would anyone want any of that? Shadow variables are usually associated with hard-to-find-bugs, (i.e. forgetting that there is already a variable named 'xyz' in the current scope) not some great feature we want more of..

FWIW I think the author is trying to simulate let binding (or lambda binding, or both). But since there’s no syntactic scope you have to do your own stack management. Ugly and clunky. But this isn’t really a language intended to be used. It’s sort of like lisp assembly.
So this language is based on dynamic scope? I'm not sure about confusing, but why wouldn't I want to just use LOGO then?
What current LOGO implementation would you recommend?
NetLogo