If I write my own shell---which I may very well do at some point---there's a
particular process model I'd like to embed in it. To wit: in UNIX right now, each
program in a pipeline has a single input stream and two output streams, with
files/sockets/&c for other kinds of communication.

A pipeline of functions `foo | bar | baz` looks kind of like this

    keyboard -> stdin     stdout -> stdin     stdout -> stdin     stdout ---+
                     >foo<               >bar<               >baz<          |
                          stderr -+           stderr -+           stderr -+ |
                                  |                   |                   | |
                                  v                   v                   v v
                                [..................terminal...................]

Which works pretty well. You can do some pretty nice things with redirecting
stderr to here and stdin from there and so forth, and it enables some nice
terse shell invocations.

I'd like that basic system to be preserved, but with the ability to easily
create other _named_ streams. For example, imagine a hypothetical version of
wc which still outputs the relevant data to stdout, but also has three other
streams with these names:

                     / newlines
                     | words
    stdin -> wc-s -> | bytes
                     | stdout
                     \ stderr

You can always see the normal output of wc on stdout:

    gdsh$ wc-s *
           2       3       6 this.txt
          10      20      30 that.c
         100    1000   10000 whatever.py

But you could also extract an individual stream from that invocation using
special redirection operators:

    gdsh$ wc-s * stdout>/dev/null bytes>&stdout
    3
    20
    1000

We could also have multiple input channels. I imagine an `fmt` command which
can interpolate named streams, e.g.

    gdsh$ printf "1\n2 3\n" | wc-s | fmt "bytes: {bytes}\n words: {words}\n nl: {newlines}\n"
    bytes: 6
    words: 3
    newlines: 2

We can then have a handful of other utilities and built-in shell operators for
manipulating these other streams:

    gdsh$ wc-s * | select words
    3
    20
    1000
    gdsh$ fmt "this is {X}\n" X<this.txt
    1
    2 3
    gdsh$ !X='cat this.txt' fmt "this is {X}\n"
    1
    2 3
    gdsh$ @X=this.txt fmt "this is {X}\n"
    1
    2 3
    gdsh$ ^Y=recidivism fmt "Y is {Y}\n"
    recidivism
    gdsh$ wc-s * words>words.txt bytes>bytes.txt newlines>newlines.txt
    gdsh$ wc -s * | split words=[sort >sorted-word-count.txt] bytes=[uniq >uniq-bytes.txt]

This could also enable new idioms for programming, e.g. verbose output, rather than being
controlled by a flag, could be output consistently on another (usually hidden) stream:

    gdsh$ myprog
    myprog: config file not found
    gdsh$ myprog stderr>/dev/null verbose>stderr
    Setting up context
    Looking in user dir... NOT FOUND
    Looking in global dir... NOT FOUND
    myprog: file not found
    Tearing down context
    Completed

Or maybe you could have human-readable error messages on `stderr` and
machine-readable error messages on `jsonerr`:

    gdsh$ thatprog
    ERROR: no filename given
    gdsh$ thatprog stderr>/dev/null jsonerr>stderr
    {"error-type":"fatal","error-code":30,"error-msg":"no filename given"}

There are other considerations I've glossed over here, but here are a few
notes, advantages, and interactions:

- I glossed over the distinction between input/output streams. In practice,
  the _shell_ has no trouble disambiguating the two, but a given _program_
  may wish to consider the distinction between `words.in` and `words.out`;
  to this end, we could rename the existing streams `std.in` and `std.out` and
  `err.out` (it being an error to read from `err.in` in most cases.[^2])

-   This is obviously not POSIX-compliant, but could be made to work with the
    existing UNIX process model by e.g. having a standard environment variable
    for stream-aware programs to look at which maps stream names to file
    descriptors. That way, programs which don't expect these special streams
    still use fds 0, 1, and 2 as expected, while programs that do handle
    these can read STREAM_DESC to find out which 'streams' correspond to which
    file descriptors. In that case, you can _almost_ use these commands with
    an existing shell by doing something like

        sh$ echo foo >&77 | STREAM_DESC='std.in:0 std.out:1 err.out:2 foo.in:77' fmt "foo is {foo}\n"

-   If we do use an existing UNIX system to write this, then we also should
    integrate libraries for this, and the API for it is unknown. Presumably
    a C interface would have `int get_stream(char* stream_name)` that could
    return -1 on failure or something like it, but maybe another interface
    would be preferable.

- This would interact really interestingly with a semi-graphical shell[^1] that
  could visualize the stream relationships between commands as well as a
  shell with a higher-level understanding of data types.[^3]

[^1]: Don't cringe. Look---the input device with the most information density
is the keyboard, right? That's why you use the command line at all. However,
graphical systems have more information density than pure-text systems. You
can take a pure-text system and extend it with position and color to give it
more information, and then with charts and graphs to give it _more_ information,
and so forth. What I'm proposing is _not_ drag-and-drop, although that might
be useful to some users; it's a keyboard-driven system that displays
information in a more dense, information-rich style. I keep thinking of building
this myself but for the massive herds of yaks I'd have to shave first.

[^2]: Actually, I can imagine a use for this. Let's say my programming language
of choice, Phosphorus, outputs its error messages in XML format, which is great
for an IDE, but I need to invoke it on a remote server which doesn't have my
IDE installed. I could
have a program `phWrapper` that passes all streams through unchanged _except_ for
`err.in`, which it parses as XML and then outputs as a kind of pretty-printed
trace representation to `err.out`. In that case,
`phosphorus my_source.ph | phWrapper` will give me the output as well as a
pretty-printed stack trace, while `phosphorus my_source.ph` will give me the
monstrous XML traces.

[^3]: PowerShell is the usual example given here, but I confess I haven't used
it. Effectively, all Unix processes expect to be given streams of raw text and
output streams of raw text. Text is versatile, which is nice, but it would
also be nice to have some basic structured data that isn't just streams of
text. For example, imagine a Lisp-based system that expected everything to be
S-expressions, so you can _guarantee_ that a program will get called with a
valid s-expression as input. Now, `wc` would output something like

        lsh$ (wc "this.txt" "that.c")
        (("this.txt" 2 4 6) ("that.c" 1 10 100))

    or a system in which the data transmitted is JSON, as in

        jsh$ wc ["this.txt", "that.c"]
        { 'this.txt': {'newlines': 2, 'words': 4,  'characters': 6  }
        , 'that.c':   {'newlines': 1, 'words': 10, 'characters': 100}
        }

    Among other things, this means that you could cut down on the amount of
serializing/deserializing that a system is doing and just pass in some raw
in-memory structure. You also get some nice operations that subsume the role
of `sed` and `awk` and its ilk, e.g.

        lsh$ (map (\ ((fname nl wd cs)) (fmt #f "%s has %d bytes." fname cs))
                  (wc "this.txt" "that.c"))
        ("this.txt has 6 bytes" "that.c has 100 bytes")

    You could then use piping and shells to do some _pretty_ elaborate stuff,
including transferring higher-level objects (like HTTP requests or opaque
OS-level objects like users or processes) in a universally understood way.

        jsh$ ps | map (fun (x) { return [x.cmd, x.args, x.pid, x.user]; })
        [ ["jsh",   [],          4440,  "gdritter"]
        , ["emacs", ["post.md"], 12893, "gdritter"]
        , ["ps",    [],          29678, "gdritter"]
        ]

So those are some ideas that have been drifting around in my head for a while.
No idea if I'll ever implement any of them, or if they'd even be worth
implementing, but I might get around to it at some point. We'll see.
