Titoo: a thought experiment

Richard Suchenwirth 2003-03-20 - "Titoo" is a fancy spelling for T2, which is a language that doesn't exist (yet), but I started thinking about it this morning. Somehow like non-Euclidian geometry evolved by just changing one of the Euclidian axioms, the idea was of a language almost like Tcl, with one difference: "A command is evaluated in two steps. First, the Tcl interpreter breaks the command into words and performs substitutions as described below. These substitutions are performed in the same way for all commands. The second word is used to locate a command procedure to carry out the command, then all of the words of the command are passed to the command procedure." (Tcl has "the first word" - hence the name T2). The second position facilitates implementation of infix operators, in a way it's coming closer to APL, but without limiting the number of arguments to 2 maximum.

Larry Smith 2003-03-25 - You might want to check out http://www.smith-house.org:8000/software/cricket.tgz . Cricket is an experimental interpreter based on TRAC which executes a very Tcl-like language, but in addition to the usual first-word-dispatching square brackets, also allows second-word-dispatching with parens. You might find it a useful testbed for experiments like these.

An exception would be if a command consists only of one word: then that of course is the name of the command, as it is in Tcl - so for instance break, continue, return, exit would need no changes.

Just to get a feeling for that, here are some Tcl and T2 commands in comparison:

 set i 0                 | i set 0
 incr i                  | i incr

Obviously, the set command would in T2 better be called "="... and incr "++"?

                         | i = 0
 expr 1 + 2              | 1 expr + 2

This is pretty silly - better release expr's operators from their dungeon, like many have done locally - a + command would add its first operand (to the left) with the second (to its right):

 + 1 2                   | 1 + 2
 expr {1+2}*{3+4}        | [1 + 2] * [3 + 4]

Like Tcl, T2 would have no operator precedence rules - explicit grouping would be required.

Some virtual code snippets how T2 might look:

 .. cd
 fp = myfile.txt open w
 fp puts "hello, file"
 fp close

 sum proc args {
   res = 0
   i in $args {res += $i}
   res =
 }
 sign proc x {
    {$x > 0} ? 1 : {$x < 0} ? -1 : 0
 }
 fac proc x {
    {$x < 2} ? 1 : {$x * [[x --] fac]}
 }
 i in [1 .. 10] {- puts $i}

Hmm.. what do y'all think?


MSW: You wrote

 fp = myfile.txt open w

which due to your evaluation rule would mean (in tcl)

 set fp myfile.txt open w

which doesn't what you intended it to do; so either have the eval rule be applied recursively until arguments are exhausted, and thus takes away the possibility to use lists as arguments to procedures (hmm, it's broken in that respect already (talking about the 'args' argument)), or you use brackets - which themselves break your evaluation rule. so to get it right, it should look like this - or a bit similar

 set fp { myfile.txt [ {open w} ] }

or something like that (you wrote open first, but open must be second, its argument is w, which must be first; the open-bracket must be second etc. All in all this eval rule breaks passing of 'args' to commands, consider this

 open myfile.txt w

open takes two arguments. Now you have to group lists explicitely

 {myfile.txt w} open

back to the set fp thing, maybe I get it right now ?

 fp = {{myfile.txt w} open} []

this could be it ... ugh, isn't that ultra-ugly ?


RS: Indeed, but that wasn't my idea: rather, in the case of open, to have the first to the left and the others to the right:

 myfile.txt open w

And yes, thought but unwritten was an explicit eval step in the = command; otherwise it should be

 fp = [myfile.txt open w]

The args argument is indeed manageable, but surprising:

 1 foo 2 3

would cause foo to be called with arguments {1 2 3}, which might end up in args if so defined.


MSW: having = an implicit eval step to the righthandside arguments would be confusing. [ breaks the eval rule, too.

 fp = {myfile.txt open w} []

should be fully according to the evil rule, no ?


MSW: Urps. Freudian ... eval is evil *hum*. meant 'eval rule' of course


Dont't tittoo me! Theo Verelst is reminded just this afternoon that the connection machine had a concept called xectors which are like associative vectors I seem to remember from a book by that name. Being an early and operational and as I recall succesfull lisp or list and association based machine. Moreover it was (is ?) a parallel machine, which is interesting, too, in these kinds of contexts, since list operations can in principle be exectuted in parallel in a natural way.

What this idea looks like is reversed polish notation, not unfruitfull in stack based languages.


jcw - Delightful thought experiment :) - I was trying to understand the semantics, but one aspect escapes me a bit:

    fp puts "hello, file"

How come the above does not need "$fp", i.e. when does name -> value dereferencing take place?

Another comment, is that this looks a lot like Smalltalk's "object msg: arg" notation, but with one key difference: in the above, "arg" is not a method, i.e. there is no OO "polymorphism" in here. Is there a nice way to make "obj1 msg arg" and "obj2 msg arg" dispatch to different implementations of "msg"?


RS: re "fp puts hello" - in my sketches I didn't expect the exact Tcl functionality to return. I thought (but didn't write) that T2's puts would do a upvar-like dereferencing on the first argument. Similarly the implicit eval with "=", etc. - an unspoken afterthought was to still conform with the eleven rules, but have the implementation reduce the need for brackets, braces, dollar signs. - Re OO syntax: there our Polish Tcl is indeed much easier to use, if the name of the object is taken as the command:

  $object say hello   ;# maybe aliased for: class::say $object hello

The T2 sketch above would in contrast need dispatchers, as if in regular Tcl we had a 'say' command that had to know the type of its object to decide on an action.


MSW hmmm...

 Tcl: set x [expr {1+2+3+4+5+6}]

How would you write that in T2 ? Okay, the righthandside would get an implicit eval, so like this ?

 x = 1 + 2 3 4 5

dunno, sure, experiments are there to be done, but this one hasn't made me too happy yet... In contrast to a pre-, in- or postfix notation, which is regular, using the "second" word imo results in more or less nicely obfuscated code if you have more than two arguments, especially as you are misled into postfix thinking when you often have things with one, or infix thinking when you have things with two arguments. A regular syntax is very important to the human mind ... a ton of special rules will not make life of us easier.


RS had thought it like this:

 x = 1 + 2 + 3 + 4 + 5 ;# calls = with x 1 + 2 + 3 + 4 + 5        assigns 15 to x
             = evals args 2..n, calls + with 1 2 + 3 + 4 + 5   returns 15 -^
              + evals args 2..n, calls + with 2 3 + 4 + 5   returns 14 -^
               + evals args 2..n, calls + with 3 4 + 5   returns 12 -^
                + evals args 2..n, calls + with 4 5, returns 9 -^  

Right-associative, I think it's called.. if we have a binary + in Tcl, this amounts to

 set x [+ 1 [+ 2 [+ 3 [+ 4 5]]]]

Certainly not the most effective way, but thought experiments need not be efficient...


MSW So if you have

 bla proc { "I" puts "say banzai" }
 x = ""
 x append banzai bla ayaken

You'll see

 I say banzai

and x will have

 "banzai ayaken"

in it ? - I mean if the '+'s in the above example are evaluated, should I write this instead ?

 x append banzai append bla append ayaken
 ? 

Sheesh, that gets confusing. What I initially meant, is that you cannot pass args as you do now as a plain list, because any expression could be the righthand side of an assignment, and thus, due to the evaluation an assignment does with its rhs, because any second word in the expression itself could be mistaken as an operator.

 x = 1 + 1 + 1 + 1

Here the second and third '+' are treated as operators.

 x proc args { [$args lindex 0] puts [[$args lrange 1 end] join " "] }

This should be the equivalent of a puts which takes a list of arguments and prints them out after joining them. but WAIT. this won't work, for it breaks the expression rule. Look at this:

 1 x 2 + 3 + 4

This really should print "1 9" because the two '+' must be interpreted as ops for compatibility with '='. Now let's add an operator "ignore" to T2, which does nothing. (. == ignore)

 1 x 2 . + . 3 . + . 4

prints "1 2 + 3 + 4". I'm just thinking of how to get rid of [s, though it seems hard. You only have each second word to express yourself ...

Back to the x proc. The right definition in this light is something like

 x proc args . { [$args lindex 0] puts [[$args lrange 1 . end] join " . "] }

Let's continue to grouping. say grouping works with ~

 sane: (1+1)*2 = 4, not 3.
 t2: 1 + 1 * 2 = 3, not 4.
 t2~: 1 ~ 1 + 2 * = 4

~ ties together expressions, repeatedly applying the last operator, so the last example is read like the one marked sane. Fine, let's use this to try and get rid of [s (and hope I don't get lost in between)

 ...

sorry, I got lost in between.. need to think about this some more, slowly wondering why I torture myself so much :p


FW: Here's a quick, horribly convoluted introspective hack (see Radical language modification, RPN in Tcl, Braintwisters) to implement second-word commands in Tcl. It doesn't work with namespaces, packages, and doesn't even allow you to define new procedures, but you can play around with this idea, at least. Making it a little less of a thought experiment.

 # Import some expr operators
 foreach op {+ - * / %} {
   proc $op {left right} {
     expr $left [namespace tail [lindex [info level 0] 0]] $right
   }
 }

 # Capture unknown commands, swap the first and second words, then re-call the command
 # in the cmds namespace.

 proc unknown {args} {
   cmds::set name [cmds::lindex $args 1]
   cmds::if {[cmds::lsearch [cmds::namespace eval cmds info commands] $name] != -1} {
     cmds::eval cmds::$name [cmds::lindex $args 0] [cmds::lrange $args 2 end]
   } else {
     cmds::return -code error "invalid command name \"$name\""
   }
 }

 # Now, mass-move all the commands to the cmds namespace.  The first argument will
 # basically always come up unknown, whatever it is, since all the commands have
 # been moved.

 foreach cmd [info commands] {if {[lsearch {unknown if lsearch rename} $cmd] == -1} {
   rename $cmd cmds::$cmd
 }}

 # Move the commands we had to leave for the moving process.

 rename if cmds::if
 rename lsearch cmds::lsearch
 rename rename cmds::rename

 # Example:

 [1 + 2] puts

GPS: Sep 06, 2003 -- Here is my version:

 % proc unknown args {set cmd [lindex $args 1] ; set args [lreplace $args 1 1]; uplevel [concat [list $cmd] $args]}
 (bin) 2 % foreach op [list * / + -] {proc $op {n1 n2} [list expr \$n1 $op \$n2]}
 (bin) 3 % 1 + 2
 3
 (bin) 4 % 4 * 4
 16
 (bin) 5 % 6 * 12
 72

FW: I'd have done it that way, but mine is far more complicated because I take steps to eliminate the possibility of when the first word is a known command.

GPS: Sep 06, 2003 -- This is unknown that works that way (use with the foreach above):

 proc unknown args { 
  set a [lindex $args 0]
  if {"" != [info commands $a]} {
   uplevel $args
  } else {
   set b [lindex $args 1]
   uplevel [concat [list $b] [lreplace $args 1 1]]
  }
 }
 (bin) 15 % + 1 6 
 7
 (bin) 16 % 1 + 90
 91

FW: No, I mean, I clear out all the commands so in the case where what someone has as their first argument happens to be a command name (not so unlikely, if you're using eval for example), it doesn't accidentally trigger the normal behavior, since I hid all the commands. So things like [+ 1 2] aren't possible, as the T2 spec implied.

GPS: Why not overload source then? That way you could force the user to only be able to use the T2 type of calls. I've done this very thing in a little project. The way it works is we rename source, and then set a global to indicate we have renamed, and finally source the script that renamed source (itself usually). I call it interpreter forking. KBK showed me how to do it.

FW: Sounds interesting. If I ever want to alter the Tcl parsing process again from softcode, I'll consult you on it <g>

Larry Smith I wonder if anyone has noticed this page re-invents the idea of dispatching messages to objects? If the first word evaluates to an object - a constant (4.5 "hello" %file1) and the second to an operator to apply to the object and the remainder arguments to that operator, what you have is the basic concept of Smalltalk. Notice also that that the logical place for the code implementing the operator to live is, in fact, in a namespace identified by the _type_ of the object - that is, in a class namespace. titoo is, in fact, smalltalk wearing a funny hat.

FW: Is it? I mean, by that token, Tcl itself is just thinly-veiled Smalltalk. T2 just creates a Smalltalk-like "phrasing" of standard Tcl.


Sarnold: I have thought of another infix-notation language, Byticle. But I gave up in favor of another language project close to Forth: Sept.


[ Hmm... there's no good category for this page. ] but it is relevant to Category Tcl Implementations