elvish/README.md

280 lines
7.7 KiB
Markdown
Raw Normal View History

# An experimental Unix shell
2013-06-16 16:45:22 +08:00
2013-09-21 21:05:27 +08:00
This is a work in progress. Things may change and/or break without notice. You
have been warned...
## Obligatory screenshots
2014-01-08 19:07:34 +08:00
> All software websites should have screenshots.
2014-01-08 19:09:34 +08:00
> -- Someone on the Internet
2014-01-08 19:07:34 +08:00
Syntax highlighting (also showcasing right-hand-side prompt):
![syntax highlighting](./screenshots/syntax.png)
Tab completion:
![tab completion](./screenshots/completion.png)
## Building
2014-01-28 20:23:50 +08:00
Go >= 1.1.1 is required. This repository is a go-getable package.
In case you are new to Go, you are advised to read [How To Write Go
Code](http://golang.org/doc/code.html), but here is a quick snippet:
```
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
go get github.com/xiaq/das
das
```
To update and rebuild:
```
go get -u github.com/xiaq/das
```
Remember to put the two `export`s above into your `bashrc` or `zshrc` (or
whatever).
2014-01-08 19:07:34 +08:00
## The Editor
2013-12-29 15:04:37 +08:00
2014-01-08 19:07:34 +08:00
Those marked with ✔ are implemented (but could be broken from time to
2014-01-08 18:52:09 +08:00
time).
2013-12-29 15:04:37 +08:00
Like fish:
2014-01-08 18:56:55 +08:00
* Syntax highlighting ✔
2013-12-29 15:04:37 +08:00
* Auto-suggestion
Like zsh:
2014-01-08 18:56:55 +08:00
* Right-hand-side prompt ✔
* Dropdown menu completion ✔
2013-12-29 15:04:37 +08:00
* Programmable line editor
And:
* A vi keybinding that makes sense
2014-01-08 19:07:34 +08:00
* More intuitive multiline editing
* Some method to save typed snippets into a script
* A "navigation mode" integrating functionalities of
2014-01-08 19:10:49 +08:00
[ranger](http://ranger.nongnu.org/)
2013-12-29 15:04:37 +08:00
## The Language
2013-12-29 15:04:37 +08:00
2014-01-08 18:56:55 +08:00
(Like the previous section, only those marked with ✔ have been implemented.)
2013-12-29 15:20:49 +08:00
2014-01-24 20:10:37 +08:00
* Running external programs and pipelines, of course (`>` represents the
prompt): ✔
2013-12-29 15:12:01 +08:00
```
> vim README.md
2013-12-29 15:20:49 +08:00
...
2013-12-29 15:12:01 +08:00
> cat -v /dev/random
2013-12-29 15:20:49 +08:00
...
> dmesg | grep bar
...
2013-12-29 15:12:01 +08:00
```
2014-01-24 20:10:37 +08:00
* Some constructs look like lisp without the outermost pair of parentheses: ✔
2013-12-29 15:07:29 +08:00
```
> + 1 2
3
> * (+ 1 2) 3
9
```
2013-12-29 15:04:37 +08:00
* Use backquote for literal string (so that you can write both single and
2014-01-08 18:56:55 +08:00
double quotes inside), double backquotes for a literal backquote: ✔
2013-12-29 15:07:29 +08:00
```
> echo `"He's dead, Jim."`
"He's dead, Jim."
> echo ```He's dead, Jim."`
``He's dead, Jim."
```
2013-12-29 15:04:37 +08:00
* Barewords are string literals:
2013-12-29 15:07:29 +08:00
```
> = a `a`
true
```
2013-12-29 15:04:37 +08:00
* Tables are a hybrid of array and hash (a la Lua); tables are first-class
2014-01-08 18:56:55 +08:00
values: ✔
2013-12-29 15:07:29 +08:00
```
2014-01-08 18:52:09 +08:00
> println [a b c &key value]
2013-12-29 15:07:29 +08:00
[a b c &key value]
2014-01-08 18:52:09 +08:00
> println [a b c &key value][0]
2013-12-29 15:07:29 +08:00
a
2014-01-08 18:52:09 +08:00
> println [a b c &key value][key]
2013-12-29 15:07:29 +08:00
value
```
2013-12-29 15:04:37 +08:00
2014-01-08 18:52:09 +08:00
* Declare variable with `var`, set value with `set`; `var` also serve as a
2014-01-08 18:56:55 +08:00
shorthand of var-set combo: ✔
2013-12-29 15:07:29 +08:00
```
> var v
> set v = [foo bar]
2014-01-08 18:52:09 +08:00
> var u = [foo bar] # equivalent
2013-12-29 15:07:29 +08:00
```
2013-12-29 15:04:37 +08:00
* First-class closures, lisp-like functional programming:
2013-12-29 15:07:29 +08:00
```
> map {|x| * 2 $x} [1 2 3]
[2 4 6]
2013-12-30 16:23:22 +08:00
> filter {|x| > $x 2} [1 2 3 4 5]
2013-12-29 15:07:29 +08:00
[3 4 5]
2013-12-30 16:23:22 +08:00
> map {|x| * 2 $x} (filter {|x| > $x 2} [1 2 3 4 5])
2013-12-29 15:07:29 +08:00
```
2013-12-29 15:04:37 +08:00
2014-01-08 18:52:09 +08:00
* Get rid of lots of irritating superfluous parentheses with pipelines (`put`
is the builtin for outputting compound data):
2013-12-29 15:07:29 +08:00
```
2013-12-30 16:23:22 +08:00
> put [1 2 3 4 5] | filter {|x| > $x 2} | map {|x| * 2 $x}
2013-12-29 15:07:29 +08:00
[6 8 10]
```
2013-12-29 15:04:37 +08:00
* Use the table `$env` for environmental variables:
2013-12-29 15:07:29 +08:00
```
> put $env[HOME]
/home/xiaq
> set env[PATH] = $env[PATH]:/bin
```
2013-12-29 15:04:37 +08:00
There are many parts of the language that is not yet decided. See TODO.md for
a list of things I'm currently thinking about.
## Motivation
2013-09-22 18:49:07 +08:00
This experiment has a number of motivations. Some of them:
2013-09-22 18:49:07 +08:00
* It attempts to prove that a shell language can be a handy interface to the
operating system **and** a decent programming language at the same time; Many
existing shells recognize the former but blatantly ignore the latter.
2013-09-22 18:49:07 +08:00
* It attempts to build a **better interface** to the operating system, trying
to strike the right balance between the tool philosophy of Unix and the
tremendous usefulness of a more integrated system.
* It also attempts to build a **better language**, learning from the success
and failure of programming language designs.
* It attempts to exploit a facility Shell programmers are very familiar with,
but virtually unknown to other programmers - the pipeline. That leads us to
the topic of the next few sections.
## Pipeline, the Good
### A Concatenative Programming Facility
Pipelines make for a natural notation of concatenative programming.
So what's concatenative programming? In some of its most common use cases, we
can say it's just functional programming without [lots of irritating
superfluous parentheses](http://xkcd.com/297/). Consider this fictional piece
of lisp to find in `strs`, a list of strings, all members containing "LOL",
transform them into upper case, sort them, and store them in another list
`lols`:
2013-09-22 18:49:07 +08:00
```
(def lols (sort (map upper-case
(filter (lambda (x) (contains? x "LOL")) strs))))
2013-09-22 18:49:07 +08:00
```
(See [Appendix A](#appendix-a) for this piece of code in real lisps.)
It looks OK until you try to read the code aloud:
> Put in `lols` what results from sorting what results from turning into upper
> case what results from filtering the strings that contain "LOL" in `strs`.
An deep hierarchy of parentheses map into a deep hierarchy of clauses. Worse,
this reads *backwards*.
What would you do it in shell, with pipelines? Assuming that the strings are
stored in the file `strs`, it is just:
2013-09-22 18:49:07 +08:00
```
lols=`cat strs | grep LOL | tr a-z A-Z | sort`
2013-09-22 18:49:07 +08:00
```
The historically weird names aside, it reads perfectly natural: assign to
`lols` the result of the following: take the lines in `strs`, find those
having "LOL", change them to upper case, and sort them. This matches our
description of the procedure except for the assignment. There is an obvious
restriction with this shell pipeline approach, but that will be the topic of
the next section.
Concatenative programming is the notion of building programs by connecting
data-transforming constructs together. In our case, the constructs are `cat
strs`, `grep LOL`, `tr a-z A-Z` and `sort`; the pipe symbol is the
connector. The interesting thing is that each construct itself is actually a
valid program; thus it could be said that a more complex program is formed by
*concatenating* simpler programs, hence the term "concatenative programming".
Compare this to the functional approach, where constructs are *nested* instead
of connected one after another.
### A Concurrency Construct
2013-09-22 18:49:07 +08:00
(TO BE WRITTEN)
2013-10-06 15:35:27 +08:00
## Pipeline, the Bad and the Ugly
2013-10-06 15:35:27 +08:00
(TO BE WRITTEN)
2013-09-22 18:49:07 +08:00
2014-01-24 11:32:52 +08:00
## Name
2013-09-21 21:05:27 +08:00
2013-09-21 21:08:54 +08:00
Indeed, **das** is not a very good name for a Unix shell. The name is actually
a corrupted form of **dash**, which also happens to be the German definite
neuter article.
2013-11-16 19:30:04 +08:00
I have some other ideas in mind. One of them is **elv**, since I found
"elvish" to be a great adjective - I can't use "elf" though, since it's
already [taken](http://www.cs.cmu.edu/~fp/elf.html) and may be confused with
the well known [file
2013-09-21 21:05:27 +08:00
format](http://en.wikipedia.org/wiki/Executable_and_Linkable_Format).
Another possible source of names is the names of actual seashells; but my
English vocabulary is too small for me to recall any beyond "nautilus", which
is both too long and already taken.
I'm not avoiding names ending in "sh" though; but I do find "bash" to be a
2013-11-16 19:30:04 +08:00
terrible name. "fish" is clever, but it has a quite [unpleasant
adjective](https://en.wiktionary.org/wiki/fishy). I find "dash" really good
though, which is why it came to my mind :).
2013-09-18 16:45:18 +08:00
2014-01-24 11:32:52 +08:00
## License
2013-07-26 14:46:24 +08:00
BSD 2-clause license. See LICENSE for a copy.
## Appendix A
This fictional lisp code:
```
(def lols (sort (map upper-case
(filter (lambda (x) (contains? x "LOL")) strs))))
```
written in Clojure:
```
(require ['clojure.string :refer '(upper-case)])
(def strs '("aha" "LOLaha" "hahaLOL" "hum?"))
(def lols (sort (map upper-case
(filter #(re-find #"LOL" %) strs))))
```
written in Racket:
```
(define strs '("aha" "LOLaha" "hahaLOL" "hum?"))
(define lols (sort (map string-upcase
(filter (lambda (x) (regexp-match? #rx"LOL" x)) strs))
string<?))
```
I'm by no means a Lisp hacker, so feel free to fire an issue if my code is not
idiomatic.