The shell is usually the interface through which the user communicates with applications and files (graphically as in Gnome Shell or textually as with bash for example). It is quite important to be able to compose the activities. In bash composing essentially means using pipes to use the output of a program as the input of another one.
Let us illustrate how important this concept is with two citations:
A long time ago, the original neckbeards decided that it was a good idea to chain together small programs that each performed a specific task, and that the universal interface between them should be text.
Ted Dziuba http://teddziuba.com/2011/10/node-js-is-cancer.html
Text streams are a valuable universal format because they’re easy for human beings to read, write, and edit without specialized tools. These formats are (or can be designed to be) transparent.
http://catb.org/~esr/writings/taoup/html/textualitychapter.html Eric S. Raymond in The Art of Unix Programming
However text is quickly problematic when data is structured: the formatting in
columns for human reading is not the best when you want to select a given
column and the width is not consistent or some cells can be empty). So
composing commands often goes through some text plumbing (making commands such
as vipe
terribly useful. Also, the scripting language associated with
shells can be quite painful to use (inconsistency: fi
ends if statements
but done
for while and for).
To account for that latter problem, two approaches have been tried:
For the former problem, having a better programming language mitigates the problem but does not solve it. ipython has an interesting way of putting output data in structured tables for intelligent grepping and cutting. Other projects solve it in their own way and explore what a modern shell can be e.g. termkit, hotwire or powershell.
Powershell is probably the first to have ventured in what an object shell can be. This makes it possible to have pipelines such as:
Get-ChildItem C:\Scripts | Where-Object {$_.Length -gt 200KB} | Sort-Object Length
Get-ChildItem
lists the content of a directory (like dir
or ls
). The result has a length attributes on which it is possible to filter or to sort.
Obviously, commands need to be rewritten, to expose an API, to define the equality of objects, which attributes are comparable and how…
ipython achieves the same effect but may be used with normal commands as it tries to be smart in interpreting what a tabular output means.
I began implementing my own shell programmed in lua (I already did a C one as a student project years ago) with the objective of having typed answers. Some choices:
/proc
.Grepping is a selection. Cutting (and analogous awk commands) is projection. Sorting is sorting. Join, combine are joins.
A shell could very well be controlled as a Relational Database.
shell is curryfied! Aliases are partial evaluations. Why should shell not be functional?
Type of commands:
cd: path -> ()
ls: Maybe path -> [[infos]]
Type of command separators:
;: ('a -> 'c) -> (() -> 'c) -> 'a -> 'c
;
is probably what Haskell’s >>
stands for.
pipe: ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
pipe is composition of commands: in haskell, pipe would be written >>=
This is better detailed in UNIX Pipes as IO Monads.
But shell is essentially variadic, which is not a functional trait. Look at the signature of zip
or tar
for example:
zip: path -> path -> path -> path ... -> path -> ()
Or logically zip: path -> [path] -> ()
.