Introducing Dawn - yet another new language

During my entire programming career (19 years) I have been interested in programming languages, how to improve and implement them. I am not formally trained in compiler theory, but have implemented a number of compilers/interpreters for a number of domain specific languages during this time. Now I am pregnant to bursting with a new language, which I need to discuss/present to someone, and I hope that LtU is the correct forum to do so (I can see from the posts that a lot of highly gifted people are lurking, so I hope to get thoughtful feedback).

The language is currently under development, so it is not fully specified. My hope was to submit papers to OOPSLA in march with a working prototype, alas it does not look like I can make it happen. Still I cannot go on for another year without releasing something about Dawn. Enough said about me and my motivations...

I will post a couple of times under this topic, with the various aspects of the language, to discuss it with the distinguished members of LtU.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Dataflow, Prototype based, no Functions!?!

Dawn is based on prototypes, and uses a dataflow like model where objects have inputs and outputs. Most dataflow based languages I have encountered uses queues for the flow, whereas Dawns inputs are like Java interfaces and outputs are like references to interfaces. This causes an input/output relationship to work like OpenGl, or SAX.
There are no functions as such, in Dawn. Conceptually, this means that data can only be passed forward in the system. Practically you can of course set a variable as recipient, thus achieving functionlike behavior.

Example (not a syntax example – just conceptual):

(1,2) >> “+” >> a      // a = 1 + 2

Erik Meijer informed me at JAOO that this is continuation passing style and monadic behavior – not being that much into the theoretical aspects, I take his word for it.
The design consideration was to have a single way of passing data, the possibility of chaining “functions”, objects and bigger blocks of functionality.

Example:

“data.xml” >> “copy.xml”		// copyFile(“data.xml”,”copy.xml”)
“data.xml” >> xmlParser >> xmlReplaceTag >> xmlWriter >> “output.xml” 

The thing to notice is that the first and last “>>” indicates a the bytewise copyinterface, whereas the 2 middle “>>” indicates a SAX like interface (startElement(name), endElement()...)
Even conditionals fit into this construct and thus becomes part of the library, instead of the basic language:

(2,a) >> “equal” >> if >> { ..block..} // if a = 2 { ..block.. }

These are still not syntax examples, just serves as a conceptual representation of how stuff gets described in the language.

That should serve as a good first provocative post – no functions for lambda the ultimate :-)

Finite Linear Flows Only?

If your language supports only linear dataflow with single inputs, then it can be (as mentioned below) implemented quite readily as a concatenative language.

OTOH, the sort of dataflow approach you describe here works best when you have 'streams' of data (or 'mutable state' if viewed as functional reactive programming) on the source side, and potentially the ability to break streams and feed them across other paths then back into one another (possibly with cycles for the generic case).

Reminds me of Icon

Not sure if this is dataflow

 (2,a) >> “equal” >> if >> { ..block..} // if a = 2 { ..block.. } 

but it reminds me of Icon.

The Icon equivalent is

write("val", (2 == a))

It uses goal directed evaluation, (the result of inner most expression determines the program flow.)
another example is

write("y=", (x | 5) > y)

which is same as

 if ((x > y) or (5 > y))
     write("y=", y)

Can you explain how 'if'

Can you explain how 'if' works? It's not clear to me from your example.

I agree with the other posts here; this looks very much like a concatenative language.

about if

Class based pseudo example of if:

class If implements booleanInput
{
  Block& then;
  Block& else;

  void setBoolean(boolean in) 
  {
   if condition
    then.go();
   else
    else.go();
  }  
}

As mentioned in another answer, when interpreting a line you first tie the inputs and the outputs together, then execute it (call go on the first object - still not actual implementation though).
Seems pretty stupid agreed, but of course in Dawn the If object is either implemented internally or has a native call peforming the actual "if condition" statement. It just goes to show how control structures are in the library. You can make a control structure that loops 10 times always for instance. Or (as I saw in another language) a switch statement that randomizes between all the valid conditions.
In effect Dawn can be used as a test bed for ideas in this field.

Have a look at the BETA language

made by the inventor of SIMULA.
BETA allows functions to return multiple values that can be passed to other functions or to a struct with the assignment operator ->
And much more.

Perhaps I'm missing something...

I hope you'll forgive me for saying so, but I fail to see how

(1,2) >> + >> a

is any different from

a = 1+2;

Just because these functions always take 1 argument and always return 1 value, or that the mechanics of invocation are peculiar doesn't really change the core behavior.

And you'll still need boolean evaluation (or some other decision mechanism) in the language to make conditionals work.

Looks concatenative

reminds me of Forth code: 1 2 + TO A

isn't really concatenative

If he is implementing true dataflow, it wont be concatenative.
i.e the below looks concatenative

(1,2) >> “+” >> a 

but (switching to the shell pipe syntax as an example of a dfl)

echo "1 2" |add > a

but if he plans to provide for streams as in a dfl
i.e

(assuming dup and add exists.)
seq 1 100| dup |add > a

it becomes very different.

(in postscript it might be)
1 100 seq [dup +] map /a swap def

The difference (aside from syntax) is that the components would be concurrent in dfl. (All this is assuming it is true dataflow.)

It is about scaling

There is not really a difference in your example. The point of the construct is that functions becomes building blocks with multiple inputs and outputs - and the building mechanism is the same. In your example, with 2 inputs and 1 output it seems pointless to do so - but let me elaborate a bit on the example. If it was div (integer division) you would have 2 outputs - the result and the remainder.

(5,2) >> div.result >> division_result        // =2
(5,2) >> div.remainder >> division_remainder  // =1
(5,2) >> div >> "+" >> result      // = 3 i.e. result = 5 div 2 + 5 % 2

Otherwise you would have to have 3 functions for optimization purposes. So the point of the exercise is to have a scaleable way of building expressions, which can be optimized by the compiler (if an output is not connected all code that is exclusive to this output can be optimized away - well thats not exactly the example)

Continuing the

Continuing the interpretation of this as a stack manipulation language, .result and .remainder seem like shortcuts for popping either the first or second element on the stack. The next question would be the definition of .result and .remainder: are they really a mapping of 'div.result' to to first and 'div.remainder' to second, and, if so, how is this determined?

Finally, >> seems to be a naming mechanism for the top element of the stack. You seem to view it as a copy / message passing operator, though this distinction does not seem to be visible within the language as presented so far, and I'm trying to not think about the runtime :)

You might want to read Backus' Turing Award speech ("Can Programming be Liberated from the von Neumann Style?") and perhaps the first few chapters of CTM (data flow variables) -- a mix of these is how I'm interpreting your language.

You haven't demonstrated the usage of prototypes (as found in Self/JavaScript); you might want to also play with Max/MSP to see what many of us typically mean by data flow (as opposed to data flow variables or concatenative languages)

Tying

I have only presented a very small part of the langauage insofar, and its definetely not playing by Neumann rules. The language can be viewed as an electronics diagram, that is (in these examples) represented by text. ">>" should more be interpreted like "tie", i.e. tie these together. This is the base of the type system, since "duck"-like rules control the tying.
It is not really of consequence if the data are passed by procedural calls, messages or other.

We have shown how to

We have shown how to directly interpret your examples using stack manipulation semantics.

I suggest presenting examples showing what's novel about your system, and, to benefit the current discussion, show dataflow manipulations that would appear differently in a stack language, and what you mean by prototypes.

However, I'm done playing 20 questions.

goodness

From hostile comments like these, you wouldn't think this was a place for people who loved programming languages.

To the contrary...

If you didn't receive passionate criticism and argument, you could be sure you were talking to an apathetic audience.

I am just tired of Paul not

I am just tired of Paul not addressing the substance of our questions nor our attempts to understand what I thought he wanted to try to communicate -- if you reread the thread, you can see why I feel trolled.

Im sorry if I failed to

Im sorry if I failed to address the questions asked – feeling “trolled” after a single post seems a little touchy to me, but well then I will just have to reply with a pretty long post to get closer to the substance. The point being that its not a language I can sum up in 3 sentences, and I was planning to do a series of posts.

Anyhow – trying to clarify what I mean with tie, here is an example of the coding style in C/C++ pseudo leading up to Dawn:

class file
{
  FILE* fileref;
  string filename:
  file* output;

  file(string _filename)
  {
    filename = _filename;
  }
  ~file()
  {
   fclose(fileref);
  }
  
 
  void begin()
  {
    fileref = fopen(filename, “r”);
  }
  void end()
  {
    fclose()
  }
  void write(char charToWrite);
  
  void go()
  {
    fileref = fopen(filename, “w”);
      output->begin();
      while (char in = fgetc(fileref))
        output->write(in);
      output->end();
   fclose(fileref);
  }

  file& operator >>(file& outputToTieTo)
  {
   output = &outputToTieTo;
   return outputToTieTo;
  }
}


(file(“inputfile.txt”) >> file(“outputfil.txt”)).go();

I hope its clear to everyone now that Im not talking about concatenative languages. The above example is NOT how Dawn is visualized implemented but shows the way objects are TIED together to achieve functionality. There is multiple calls between the 2 objects not just stacks.
It is a 2 step operation, when you interpret a line – first build the line by tying the objects together, then run them. You might even go so far to say that it is declarative since you describe the pipe.
The central idea is that the objects in question can be implemented as threads, as processes, as processes on different machines or simply as methods in objects.
The example is using a class based language to illustrate one of the main ideas in Dawn.
Another main idea is the lookup of identifiers. In a “normal” language the compiler or interpreter decides how to lookup identifiers. In dawn all the objects inherit the lookup from the primordial object, and thus allows the lookup to be overridden. An example of this behavior is ruby's method_missing exception. But instead of hiding the lookup, and only throwing exceptions when methods are not found, Dawn allows the user to override the behavior complete.
This means that you can write (provided that libraries internet and file exist):

“internet.http://google.com/search lambda-the.ultimate“ >> file.searchresult.txt

Dawn blurs the boundary between compiletime and runtime; the internet lookup example above can be performed at different times: at compile time, at runtime – cached, or every time the statement is passed. This mean that you can include buildtime in your program automatically.
Dawn grows out of trying to get rid of all the external scripting languages to perform builds and all the internals extras like generics. Dawn tries to be its own scripting language and its own meta language. That is why it is difficult for me to sum it up in few words. That is why I need to finalize my prototype since there are a few practical problems still to be designed/resolved.
This post probably raises more questions than it answers, hopefully I have conveyed a little of my idea.

how to explain the parts of the elephant

Dawn tries to be its own scripting language and its own meta language. That is why it is difficult for me to sum it up in few words.

perhaps describe the layers from bottom to top, individually.

Policy

Im sorry if I failed to address the questions asked – feeling “trolled” after a single post seems a little touchy to me, but well then I will just have to reply with a pretty long post to get closer to the substance. The point being that its not a language I can sum up in 3 sentences, and I was planning to do a series of posts.

A series of posts is not appropriate. A better way to present the information would be to put the information on a web page or blog and then post a link to it here and the questions you have. This will improve the quality of the discussion.

If you don't have anywhere to host it currently, it is easy enough to set up a blog or website for free.

Prototypes, Owners and References

Dawn does not have garbage collection, although it is possible to implement for memory only data. Dawn has the concept of an owner, that any object is owned by another object (except for the top one). All objects have a name, if an anonymous object is desired it is automatically given a name.
A reference to an object, can be translated into text (in the spirit of URL, based on the name). The owner reference is thus the package, the namespace and the scope.
A reference is not guaranteed to be valid, although the language will provide safeguard against crashes.
Reference types can be implemented in the library (this is the novel thing – I was planning for OOPSLA - more about that in a later post), so it is possible to reference other things than memory objects (well – it looks like that for the user).

So you can go:

“dawn” >> “internet.http://www.google.com//search” >> pdfConverter >> “localfile.pdf”

..I know that this raises a lot of questions, but its hard to sum up years of considerations into a few posts. Hopefully I will soon have the prototype up and running, so I can be more concrete.

I was myself a little surprised about not having garbage collection, but for GC you need to control all the resources, and I wanted to be able to reference a file, an internet site or a USB drive, and there is no guarantee that they are available. So I want the programmer to get use to using patterns to deal with inconsitent/unavaliable resources, regardless if they are internal or external.

Please use "reply"

When replying to another comment, please click the "reply" link under the comment to maintain the conversation's thread.

I'm shooting in the dark,

but can your language be described as "reactive"?

You haven't described the behavioural semantics of your expressions, so which interpretation does this have?

“internet.http://www.google.com/” >> “localfile.htm”

Does it mean:

  1. Poll "http://www.google.com/" for changes; when it changes, push the content into "localfile.htm"?
  2. Subscribe to some on-page-change event notifier for "http://www.google.com/", and when the page changes, the notifier will notify the node for "localfile.htm", which will then pull the content from "http://www.google.com/" and push it into "localfile.htm"?
  3. Pull "http://www.google.com/" and push it into "localfile.htm" once?

The same goes for "if". If I got it right, don't call it "if", which confuses your "if"'s semantics with tradition. Call it "when":

"When X, do Y"

If I understand correctly, you want code to reactively execute in response to a condition becoming true.

I must say! An internet dataflow framework is a very handy-dandy idea (not to mention the desirability of having reactive file re-computations for automated content deployment).

I had ideas for a "reactive" dataflow programming language a couple years back, but I had never considered messing with files, or actually leveraging the potential of the internet. Nothing ever came of the idea (being lazy and all).

Kudos for:

  1. Having fresh ideas.
  2. Not being too lazy to do something about them!

I watch with interest!

An example from a library I

An example from a library I wrote one summer for just that: http://www.flapjax-lang.org/demos/delicious/ (I don't like plugging my work when trying to discuss that of others, but your example was pretty close to some code I wrote).

I think Poul's *intended* language is like Ptolemy, except ambiguous in defining how actors can be connected (impacting global guarantees), and also introduces an unsafe prototype-based language for building base actors (Ptolemy used C++ and switched to Java). Of course, the examples provided are insufficient to back up this claim. If this is accurate, I'd suggest Poul to check out Ptolemy: the team created a great modeling environment for actor/data flow languages that provides reusable IDEs and components, as well as shows a lot of the design space for semantics, including how to nest models.

Finally, Winheim, some bio guys made a firefox extension for Ptolemy, which might give a flavor of how this might work in practice, if you're really need that itch scratched.

Ptolemy

Ptolemy is very much in family with Dawn. I aim for the posibility of a graphical frontend as an alternative way of editing, and Ptolemy's IDE looks pretty much like that. Will definitely be checking it out.

Reactive

Well, I do not imply a permanent relationship. If is actually if - when the program counter passes that statement.
About the interpretation - in several steps:

  1. lookup id "internet.http://www.google.com/"
    this calls the lookup method for the current scope.
    Default behavior is to look through its own slots, its parents slots (inheritance), its owners slots and throughout all this lookup a relative path is used (.system).
    So in the end it finds at the top an object called system.internet. This object "returns" a http object "constructed" with the http address - you can say that what comes after the . is used a parameters for the constructor.
  2. lookup id "file.localfile.htm" (there was a bug in my example)
    Similar this one finds system.file with returns a file object constructed with localfile.htm
  3. these 2 objects are tied together (using a set of rules for matching inputs and outputs)
  4. the http object gets "called" - it will stream the http page bytewise to the file (they share the bytewise interface - but there may be other more highly interpreted interfaces - the http object will probably also support an xml interface - like SAX)

About If - well yes it shouldnt have been called If in the example - but in the real Dawn syntax it SHOULD be called if - since it will be the implementation of if.

There is several ideas embedded in Dawn - that you have only one mechanism (tie) you use, both if you tie the expression, the conditional and the "then" and "else" blocks in an "if", or if you tie an entire webpage (site even) with some other block of logic.

You build fat objects with lots of outputs - the outputs can share computation, but the compiler can also trim unused computation away for unused outputs.

Reuse should be encouraged since object get fatter and the programmer cannot choose how to "return" values.

Finally the path in the lookup, allows for overriden fundamental behavior in a controlled manner. If you want to change the behavior of "if" in some object you just place an system.if object in your project root! This allows you to change the behavior of the black box without opening it!! It is an extremely powerful mechanism, that replaces the generics, aspectoriented and a lot of other features of langauges.

I think I get it now.

Based on what I gleaned from your posts, your language is NOT an online system, but is simply like the Unix pipes: run-once. (I feel silly now.) Basically, you have invented a different syntax as a replacement for common procedural syntax, or for the unix pipes syntax. As people have said before (and it didn't quite click before), your language is concatenative in the semantic sense.

This:

"internet.http://www.google.com/" >> "file.temp.txt"

becomes this:

save(readPage("http://www.google.com/"), "temp.txt")

given the routines:

function readPage(URL: string): TextData;
procedure save(input: TextData; filename: string);

The big difference is in the implementation of the interpreter.

What's novel about your system is, as you say, your ability to override language constructs via your scope mechanism. Your language essentially provides a scope hierarchy via prototypes. Does it provide dynamic scope via mutable prototypes (at runtime)? If so, that opens up a can of worms, but I like the idea, provided such insane cases are generally avoided in practice.

This calls for a comparison between your method and that of generics, AOP, etc. for flexibility, expressiveness, power, etc.

Concatenative

Unix pipes is a big inspiration, yes. But there is a difference - maybe it is only semantic:

"internet.http://www.google.com/" >> "file.temp.txt"

does not becomes this:

save(readPage("http://www.google.com/"), "temp.txt")

because this uses a stack to transfer values There is not a single pipe, there is multiple outputs - the syntax covers the unix pipes, but is also covers an entire interface, such as OpenGL or SAX. It goes to the method of passing values.

not

Integer function() {return 2);

but

procedure() {output->setInteger(2);}

This allows output to be directed to diffent places i.e:

(5,2) >> div.(.result >> console, (2, .remainder) >> add >> console)

Here the console will print 2 and 3.
I may be wrong, but I dont see concatenative languages work like that?

"Only semantic"

Confusingly, PL people use "semantics" differently from everyone else (or at least with a different emphasis). In ordinary usage, "we're arguing semantics" means that we're just arguing over the mapping from words to concepts, but we both have the same concepts in mind. In PL circles, "we're arguing semantics" generally means that we're arguing over the concepts themselves -- pretty much the opposite of its everyday meaning.

multiple return values

Well, in FORTH (FORTH is concatenative?):

    5 2 /MOD . 2 + .

Multiple return values

Well maybe im not clear in the destinction. It is NOT return values. Maybe the effect can be simulated by return values - but the whole point is that outputs are not nessecarily fired. The object performing "IF" fore instance, only calls "THEN" or "ELSE". It does not "return" anything. My bad is maybe with these small examples. If you take OpenGL as the input interface, you can have multiple objects "piping" into it:

triangle >> OpenGl
"model.vrml" >> OpenGL
0.5, "model.vrml" >> GLScale >> OpenGL
"model.vrml" >> SortBlackPolygons.(.NonBlack >> OpenGl, .Black >> "file.txt")

The last example would read model.txt from file, pipe it via the OpenGL calls (glBegin,glEnd,glVertex,glColor etc.). Nonblack polygons will be rendered, Black will go into the file.
The SortBlackPolygon may be an extremely fat function with hundreds of outputs where the user can connect to the outputs he need. The compiler can optimize functionality so only code needed for these outputs are generated - and functionality can be shared between these functions. An output does not nessecarily pass a value - it may just pass the program counter (IF) - return values would be a specialized boolean of sorts - execute this. This means that it will always have to evaluate the full function, and always return all "outputs" - ie. return values.
Well i can see that my concept is closely related to the concatenative, although im not exactly sure if they are conceptually identical.

I see.

So, your expressions remind me alot of of Ruby, and its "fluid interfaces". Your language has concatenative semantics only when you use simple expressions, but when you start using multiple "data-feeds", then your language does something that would have to be done very differently in a concatenative language, so your language is not strictly concatenative. To say it is, is a stretch, at the least. It can do concatenative stuff in the simple cases (when you're not using the multiple output parts of objects in an expression). I'm not sure if your language would be considered a superset of a concatenative language, but I'm guessing not; your language goes down a somewhat different branch of the language design space.

Thanks

Part of posting here is to get a little closer to established theory so I can talk the talk with other language people.
So its great help to get inputs about the different language theories.
Another example I think is not concatenative:

100 >> times >> {"hello world" >> console}

Ruby is of course also a big influence.

Switch the two right

Switch the two right expressions and it is :)

(I'm not actually sure why it works in yours)

The next thing I'd want to know is actually how it compares to Oz and how data flows are restricted (which is crucial fir many data flow compiler tricks). For the latter, looking at various Ptolemy semantic models is one way.

Semantics might help

It might help if you provide a description of the intended semantics of a code fragment when you post it. You've done that in some of your posts, but not all of them. The posts where you have defined the intended meaning of a code fragment are the ones that have made it most clear what it is you're trying to get at.

The post to which this is a response is a good example of how not to present your language: I'm left to infer what I think the meaning of your code fragment is, based on the assumption that your intention is a Ruby-like result here (despite the fact that you're apparently reversing the Ruby would handle things, instead passing a "100" message to a "times" object). But if I don't make the assumption about Ruby-like semantics it's hard to know what to make of the code. You might be presenting a weird multiplication, or a strange way of concatenating "100" with the current time and sending the resulting string to the console, or...

By telling us what (in your mind) the code is intended to accomplish, and the mechanism used to do so, you're more likely to be able to communicate why Dawn is special. By just posting unexplained fragments of code, you leave people guessing, and mapping the syntax to their own semantic models (which can lead to confusing misinterpretations, and much talking past each other). It doesn't have to be a formal operational semantics. Just a brief description of what the string of symbols you're showing us is actually intended to do.

Wrong presentation

Yes, I can see that it has not been presented in presented in the best of manners. It was probably premature of me to start presenting something in fragments, so I might stand down from the presentation until my prototype is done - then I can write a more in-depth description of the ideas.
Just hoping to convey a little extra information concerning the piping - historical and technical:

The "piping" syntax of Dawn was invented because I have been programming in a manner, which I felt had some big benefits, but was tedious to do with current language syntax. Ideas come from Flow Based Programming, and electronic gates and block diagrams, but with an object oriented approach. My approach was first developed in 1995 at LEGO where I needed a way to transport LEGO models from one program to another. Several third party companies were developing LEGO games so how would I let them exchange models?
The standard way would be a XML DOM-like way where you build a memory model of the LEGO model, and then lets 3rd party convert to-from this model. However in 1995 memory was very much an issue, especially for 3d games, so I came up with a SAX/OpenGL like model where I streamed data. This allowed for 3rd party IMPORTING data could build their own structures based on callbacks. When EXPORTING data they would sit on the other side of the interface and call the methods for building the model. The interface functions where like: beginModel(name),endModel(), position(x,y,z),rotation(x,y,z,t).
Models could be defined within models making it hierarchical.
I found that I could write processors which had that same interface for input and output that could manipulate the models and change their appearance in different manner. This lead to objects, stringed together in a pipe like manner, usually starting with an object reading a file, interpreting it and streaming it via the interface, through these objects, ending with an object writing a file - thus ending up with a converter.
Since 1995 I have developed this method to be totally general for data processing, still lacking a proper language to support it.
So when I write:

100 >> times >> {"hello world" >> console}

I mean a default integer object constructed with 100, with an integer method passing output reference, this set to point to a class times object (which has a integer method to set the times parameter), calling the block method of the anonymous block, where a default string objects, with a string method passing output reference, set to point to the consoles method for passing strings.
I feel that current languages are not really supporting big blocks of code being tied together, and it is this type of hierarchical generality I am trying to achieve.

Now I will look into Oz, which I havent quite gotten from the wikipedia entry (but the lazy/earger is apparently identical to Dawn), and ptolemy, try to finish my prototype.
And then Ill be back (don't know if its a threat or a promise :-)

Similar to Yahoo! Pipes?

Would you say that this is almost a text representation of Yahoo! Pipes? Multiple inputs that can into an aggregator, which may have multiple outputs that can get piped all over the place?

Now, in this example, I don't see why "SortBlackPolygon" couldn't just be a generic function that returns a tuple (vector, hash, list, whatever you want). I do, however, see the benefit when you have hundreds of possible outputs. Unfortunately, I can't really think of an example where something might have a couple hundred outputs -- and when it might, it always seems better to aggregate them into a structure of some sort.

Where do you see the real benefit of this model?

Also, what is the execution model? When I pipe something into SortBlackPolygon, is it executed immediately? Or only when I ask for the out pipe? What if I ask for one out pipe then another later down in the program? What happens if I want to use .NonBlack in the current line and connect the .Black pipe a couple lines down?

Yahoo! Pipes

Yes, it is a textual representation of something like that. Im planning to hav multiple representations of the source and Yahoo! Pipes is definetly a good example of how the graphical representation can look like.

Generally things are executed immediately. A single pipe can be specified to be a thread, allowing for a more "reactive" style.

You cannot "ask" for the out pipe - the model is push, not pull. Conceptually you setup the pipe and call the first element of the pipe, which then "does its thing" and returns when done. If its a thread, the next pipe starts processing without waiting.

The examples of 100's of outputs is maybe not at obvious as the 100's of inputs. OpenGL is a good example - the polygon interface is a small subset of the entire interface (=inputs).

The point of using outputs and inputs is that outputs are not nessecarily "fired". You could return a tuple with objects or strings signifying the outputs called, but then it is an implementation issue.
Besides it is possible to "wire" outputs from an objects to different objects, so in the case of returning a single tuple, it would take a "administrator" to redirect outputs to the correct inputs.

The benefits of this model IMHO is that you use the same method of relaying data/describing your functionality irregardless if you are making control structures, calling functions or tying together SOAP-like services, much higher in the hierarchy.

I consider

Game >> OpenGl! // exclamation means thread
1,1 >> Add
100 >> Times >> PrintHello

an advantage over most other languages which would have very different constructs for these.

That said, I still have a number of practical issues to address, especially about using the prototypes as classes - i.e. when you refer to SortBlackPolygon, do you get the same instance or is it a copy - can you decide yourself if its a copy?
If its not a copy, I will have big challenges for multithreading, if it is a copy - well, how do I manipulate then the original?
Well a lot of this is implementation, and thus not for LtU discussion.