github.com/xiaq/elvish@v0.12.0/website/src/learn/fundamentals.md (about)

     1  <!-- toc -->
     2  
     3  **This tutorial is quite incomplete, and it is being constantly expanded.**
     4  
     5  This tutorial introduces the fundamentals of shell programming with Elvish.
     6  It does not assume familiarity with other shells, but some understanding of
     7  basic programming concepts is required.
     8  
     9  This tutorial evolves around a "hello" program. But if your primary interest
    10  in shell programming is not saying hello (which, by the way, is a shame), the
    11  tutorial also contains some hints and examples on how to apply to knowledge to
    12  non-hello applications.
    13  
    14  **Note**: Elvish is very similar to other shell languages in many aspects, but
    15  also very different in others. When transferring your knowledge of Elvish to
    16  another shell, it is worthwhile to first check how things work there.
    17  
    18  
    19  # Hello, world!
    20  
    21  Let's begin with the most traditional "hello world" program. In Elvish,
    22  you invoke the `echo` **command** to print something on the terminal:
    23  
    24  ```elvish-transcript
    25  ~> echo "Hello, world!"
    26  Hello, world!
    27  ```
    28  
    29  In Elvish, as in other shells, command invocations follow a simple structure: you
    30  write the command name, followed by arguments, all separated by spaces (or
    31  tabs). No parentheses or commas are needed.
    32  
    33  We enclose our text here in double quotes, making it a **string literal**.
    34  Compared to other languages, shell languages are a bit sloppy in that they
    35  allow you to write strings *without* quotes. The following also works:
    36  
    37  ```elvish-transcript
    38  ~> echo Hello, world!
    39  Hello, world!
    40  ```
    41  
    42  However, the way it works has a subtle difference: here `Hello,` and `world!`
    43  are two arguments (remember that spaces separate arguments), and `echo` joins
    44  them together with a space. This is apparent if you put multiple spaces
    45  between them:
    46  
    47  ```elvish-transcript
    48  ~> echo Hello,      world!
    49  Hello, world!
    50  ~> echo "Hello,     world!"
    51  Hello,     world!
    52  ```
    53  
    54  When you write your message without quotes, no matter how many spaces there
    55  are, it is always the same two arguments `Hello,` and `world!`. If you quote
    56  your message, the spaces are part of the string and thus preserved.
    57  
    58  It is a good idea to always quote your string when it contains spaces or any
    59  special symbols other than period (`.`), dash (`-`) or underscore (`_`).
    60  
    61  
    62  ## It Doesn't Have to be Hello
    63  
    64  All command invocations in shell have the same basic structure: name of
    65  command, followed by arguments. Elvish provides a lot of useful [builtin
    66  commands](/ref/builtin.html), and `echo` is just one of them. As another
    67  example, there is one for generating random numbers, which you can use as a
    68  digital dice:
    69  
    70  ```elvish-transcript
    71  ~> # randint a b generates an integer from range a...b-1
    72     randint 1 7
    73  ▶ 3
    74  ```
    75  
    76  Arithmetic operations are also commands. Since they also follow the same order
    77  of command name first, the syntax deviates a bit from usual mathematical
    78  notations:
    79  
    80  ```elvish-transcript
    81  ~> * 17 28 # multiplication
    82  ▶ 476
    83  ~> ^ 2 10 # exponention
    84  ▶ 1024
    85  ```
    86  
    87  The commands introduced above -- `echo`, `randint`, `*` and `^` -- are all
    88  **builtin** commands, commands that Elvish provides for you.
    89  
    90  As a shell language, however, Elvish also makes it trivial to use **external**
    91  commands, commands implemented as separate programs. Chances are you have
    92  already used some of them like `ls` or `cat`. Here we show you how to obtain
    93  Elvish entirely from the command line: you can use `wget` to download files,
    94  `shasum` to verify its checksum, and `tar` to uncompress them, all of which
    95  are external commands:
    96  
    97  ```elvish-transcript
    98  ~> wget https://dl.elv.sh/elvish-linux-amd64-HEAD.tar.gz
    99  ... omit ...
   100  elvish-linux.tar.gz  100%[======================>]   4.91M  10.9MB/s    in 0.4s
   101  ~> shasum -a 256 elvish-linux.tar.gz
   102  0fc3c145a81345a1c49576b86ef12156d4eba1829e1bb20e9c39d115991a9c7b elvish-linux.tar.gz
   103  ~> tar xvf elvish-linux.tar.gz
   104  x elvish
   105  ```
   106  
   107  With the most basic knowledge of how to invoke commands, there is already a
   108  myriad of functionalities at your fingertip.
   109  
   110  
   111  # Hello, {insert user name}!
   112  
   113  The "hello world" program is a classic, but the fact that it always prints the
   114  same simple message does make it a little bit boring.
   115  
   116  One way to make programs more useful is to teach them to do different things
   117  depending on the context. In the case of our "hello world" program, why not
   118  teach it to greet whoever is running the program?
   119  
   120  ```elvish-transcript
   121  ~> echo "Hello, world! Hello, "$E:USER"!"
   122  Hello, world! Hello, xiaq!
   123  ```
   124  
   125  There are several things happening here. First, `$E:USER` represents the `USER`
   126  **environment variable** ("E" being mnemonic for "environment"). In UNIX
   127  environments, it is usually set to the name of the current user. Second, we
   128  are running several strings and a variable all together: in this case, Elvish
   129  will concatenate them for you. Hence the result we see.
   130  
   131  Depending on your taste, you might feel that it's nicer to greet the world and
   132  the user on separate lines. To do this, we can insert `\n`, an **escape
   133  sequence** representing a newline in our string:
   134  
   135  ```elvish-transcript
   136  ~> echo "Hello, world!\nHello, "$E:USER"!"
   137  Hello, world!
   138  Hello, xiaq!
   139  ```
   140  
   141  There are many such sequences starting with a backslash, including `\\` which
   142  represents the backslash itself. Beware that such escape sequences only work
   143  within double quotes.
   144  
   145  Environment variables are not the only way to learn about a computer system;
   146  we can also gain more information by invoking commands. For instance, the
   147  `uname` command tells you which operation system the computer is running:
   148  
   149  ```elvish-transcript
   150  ~> uname
   151  Darwin
   152  ```
   153  
   154  ([Darwin](https://en.wikipedia.org/wiki/Darwin_(operating_system)), by the
   155  way, is the open-source core of macOS and iOS.)
   156  
   157  To incorporate the output of `uname` into our hello message, we can first
   158  **capture** its output using parentheses and keep it in a variable using the
   159  assignment form `variable = value`:
   160  
   161  ```elvish-transcript
   162  ~> os = (uname)
   163  ~> # Variable "os" now contains "Darwin"
   164  ```
   165  
   166  We can then use this variable, in a similar way to how we used the environment
   167  variable, just without the `E:` namespace:
   168  
   169  ```elvish-transcript
   170  ~> echo "Hello, "$os" user!"
   171  Hello, Darwin user!
   172  ```
   173  
   174  I used a variable for demonstration, but it's also possible to forego the
   175  variable and use the captured output directly:
   176  
   177  ```elvish-transcript
   178  ~> echo "Hello, "(uname)" user!"
   179  Hello, Darwin user!
   180  ```
   181  
   182  ## It Doesn't Have to be Hello
   183  
   184  Output captures can get you quite far in combining commands. For instance, you
   185  can use output captures to construct do complex arithmetic involving more than
   186  one operation:
   187  
   188  ```elvish-transcript
   189  ~> # compute the answer to life, universe and everything
   190     * (+ 3 4) (- 100 94)
   191  ▶ 42
   192  ```
   193  
   194  
   195  <!--
   196  # Hello, everyone!
   197  
   198  
   199  Now let's say you want to say hello to several people, and typing `Hello` repeatedly is tiresome. You can save some work by using a **for-loop**:
   200  
   201  ```elvish
   202  for name [Julius Pompey Marcus] {
   203      echo 'Hello, '$name'!'
   204  }
   205  ```
   206  
   207  In elvish you can put newlines between the elements to loop over, as long as they are terminated by `; do`.
   208  
   209  For easier reuse, you can also create a **list** to store the names:
   210  
   211  ```elvish
   212  triumvirate = [Julius Pompey Marcus]
   213  ```
   214  
   215  Lists are surrounded by square brackets, like in several other languages. Elements are separated by whitespaces.
   216  
   217  As you may have noticed, dashes are allowed in variable names. You are encouraged to use them instead of underscores; they are easier to type and more readable (after a little getting-used-to).
   218  
   219  Now it's time to use our list of the first triumvirate:
   220  
   221  ```elvish
   222  for name in $first-triumvirate; do
   223      echo 'Hello, '$name'!'
   224  done
   225  ```
   226  
   227  This will, however, results in an error, saying that a string and a list cannot be concatenated. Why? Remember that `$x` is always one value. This is even true for lists, so the `for` loop only sees one value to loop over, namely the list itself.
   228  
   229  To make multiple words out of a list, you must explicitly **splice** the list with an `@` before the variable name:
   230  
   231  ```elvish
   232  for name in $@first-triumvirate; do
   233      echo 'Hello, '$name'!'
   234  done
   235  ```
   236  
   237  # Each person gets $hello~'ed
   238  
   239  The for-loop we just show can also be written in a functional style:
   240  
   241  ```elvish
   242  each [name]{
   243      echo 'Hello, '$name'!'
   244  } $first-triumvirate
   245  ```
   246  
   247  This looks similar to the for-loop version, but it makes use of a remarkable construct -- an **anonymous function**, also known as a **lambda**. In elvish, a lambda is syntactically formed by an argument list followed immediately (without space) by a function body enclosed in braces. Here, `[name]{ echo 'Hello, '$name'!' }` is a lambda that takes exactly one argument and calls `echo` to do the helloing. We pass it along a list to the `each` builtin, which runs the function on each element of the list.
   248  
   249  Functions, like strings and lists, can be stored in variables:
   250  
   251  ```elvish
   252  hello=[name]{ echo 'Hello, '$name'!' }
   253  each $hello $first-triumvirate
   254  ```
   255  
   256  To call a function, simply use it as a command:
   257  
   258  ```elvish
   259  $hello 'Mark Antony' # Hello, Mark Anthony!
   260  ```
   261  
   262  You must have noticed that you have to use `$hello` instead of `hello` to call the function. This is because the *hello-the-variable* and *hello-the-command* are different enitites. To define new commands, use the `fn` special form:
   263  
   264  ```elvish
   265  fn hello [name]{
   266      echo 'Hello, '$name'!'
   267  }
   268  hello Cicero # Hello, Cicero!
   269  ```
   270  
   271  Users of traditional shells and Common Lisp will find this separation of the variable namespace and command namespace familiar.
   272  
   273  However, in elvish this separation is only superficial; what `fn hello` really does is just defining a variable called `hello~`. You can prove this:
   274  
   275  ```elvish
   276  echo $hello~ # <closure ...>
   277  $hello~ Brutus # Hello, Brutus!
   278  each $hello~ $first-triumvirate # (Hello to the first triumvirate)
   279  ```
   280  
   281  Conversely, defining a variable `hello~` will also create a command named `hello`:
   282  
   283  ```elvish
   284  hello~ = [name]{ echo "Hello, hello, "$name"!" }
   285  hello Augustus # Hello, Augustus!
   286  ```
   287  
   288  <!--
   289  ```
   290  What I want to get into this document:
   291  
   292  [ ] Command substitution
   293  
   294  [ ] Rich pipeline
   295  
   296  [X] Lists
   297  
   298  [ ] Maps
   299  
   300  [X] Lambdas
   301  
   302  [X] fn
   303  
   304  [X] $&
   305  
   306  [X] One variable, one argument
   307  
   308  [X] String syntax
   309  
   310  [X] Lack of interpolation
   311  
   312  [X] Several builtins -- each println
   313  
   314  [ ] Editor API
   315  
   316  [ ] Exception and verdict
   317  
   318  [X] E: namespace for environment variables
   319  
   320  [ ] e: namespace for external commands
   321  
   322  [ ] Modules
   323  
   324  Write for readers with a moderate knowledge of a POSIXy shell (bash, zsh, ...)
   325  -->
   326