Skip to the content.

0.6.0: new streams system

Until now, streams was something running kind of outside the “normal” way values were passed around inside Til VM. The results were also awful, since commands that generated data could also return values (that is, push them to the stack), and these values would end up “hanging around” in the stack whenever they was called in the middle of a Pipeline, so I was forced to “zero” the stack at end of each one - a very “patchy” approach.

Now, if your command want to pass along “continuous” data, as lines from a file, bytes from a socket or messages from a queue, it must implement an “iterator” (I’ll probably improve on that name) that is going to be pushed to the stack. Then, Pipeline.run is going to make sure that context.hasInput is true, so the next command can decide what to do.

Ignoring context.hasInput, from the point of view of the next command, the iterator seems just like any other argument, being the last to be passed to the command and, therefore, the last to be popped from the stack.

In some cases, it’s vital to check for hasInput. spawn is a good example: if the command ignore this flag, how is it going to decide if the last argument is an iterator or is actually an argument to be passed to the command being spawned?

An iterator is a regular ListItem (or simply Item) and must implement a method CommandContext next(CommandContext). Each new item from the current iteration must be pushed to the stack and the exitCode may be Continue, indicating there is a new item pushed to the stack and that the next command in the pipeline should also “continue” iterating or Break, meaning the end of the iteration was reached.

I really liked this new approach, as it seems more fit with the workings and idioms of the rest of the VM while also easing the process of creating a new iterator class (now you only need to implement one method).

No more pushing data

There was a special command that made me specially discomfortable: write. In Til, data should be always pulled, not pushed. But when running a procedure “in background” in the middle of a Pipeline, you would like to write things into the next pipe, right? And trying to implement that in such a way that things would behave kind of in the same way as if you were implementing a stream in the old way (with ranges), I ended up creating a special “process i/o range” that would allow the background process to write one item and forced it to wait until it was consumed by the pipeline in the original process.

Well, no more. Now, if you spawn a new process, the output of this new one will be a Queue.

“Run in background”

Previously I had added a & token that, being present in the end of a Pipeline, would indicate that it should be run “in background”, that is, scheduled in another Process in the Scheduler. The sole purpose was to make it a little easier to use Til-defined procedures in the middle of Pipelines.

I didn’t enjoy that syntax change very much, since the idea of Til is being a “command language”, without much syntax to be learned. Also, the presence of “pipes” plus a & sign makes it way too much similar to Shell Script, something that may be wise to avoid for now (some people seems to understand “being an alternative to Shell Script” as an objective of the language, and that is not the case).

Also, this notion of “in background” kinds of clouds the concept of the Scheduler, and I think it’s nice to have this later concept very clear in the programmers minds (see how difficult it is for some Python developers to grasp the concepts of async in Python…).

I started all this bunch of changes by trying to make a Til-defined procedure in the middle of two pipes to automatically run “in background”, but now I see how detrimental it may be for the clarity of the whole program, specially now that new processes do not share the main process input and output (I’m playing with this concept of making the “main process” a place with some specific purposes for the programmer).

Now this kind of thing is the role of transform. If you have Til-defined procedures you want to use in your pipelines, you must call them using transform and yes, for now we’re going to live with that. In the end, I really want to incentivise programmers to create their modules in D, not Til itself, so the language is not going to become that much more complicated to try to mimic in Til everything that can be done in D.

Simplicity and next steps

I’m happy that I was able to get rid of a lot of code and, overall, the language (I mean, the implementation of the language) has become more simple and easy to understand.

For the next step, my plan is to work on the object orientation support of Til. Initially I was tempted to not go this route, but (i) I really enjoy programming with classes and objects and (ii) that’s inevitable. If I’m not the guy doing that, someone else will be. So I prefer to implement that now and avoid the proliferation of too much alternatives, that I also see as detrimental to the whole “ecosytem” of any language.