aboutsummaryrefslogtreecommitdiffstats
path: root/docs/guide
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-17 15:41:56 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commit406da94f13f47a9c7aef6b9c11cfe057772630cf (patch)
treedd98e12eddd421e2883c90239cb10225bb75b07e /docs/guide
parentbuiltin/trae: print literals literally (diff)
downloadalive-406da94f13f47a9c7aef6b9c11cfe057772630cf.tar.gz
alive-406da94f13f47a9c7aef6b9c11cfe057772630cf.zip
split guide into multiple pages
Diffstat (limited to 'docs/guide')
-rw-r--r--docs/guide/basic-types.md59
-rw-r--r--docs/guide/copilot-gui.pngbin0 -> 4445 bytes
-rw-r--r--docs/guide/defining-symbols.md37
-rw-r--r--docs/guide/evaltime-and-runtime.md45
-rw-r--r--docs/guide/functions.md54
-rw-r--r--docs/guide/getting-started-guide.md25
-rw-r--r--docs/guide/hello-world.md33
-rw-r--r--docs/guide/importing-operators.md26
-rw-r--r--docs/guide/installation.md53
-rw-r--r--docs/guide/making-sound.md58
-rw-r--r--docs/guide/scopes.md82
-rw-r--r--docs/guide/syntax.md43
-rw-r--r--docs/guide/working-with-the-copilot.md59
13 files changed, 574 insertions, 0 deletions
diff --git a/docs/guide/basic-types.md b/docs/guide/basic-types.md
new file mode 100644
index 0000000..c22929c
--- /dev/null
+++ b/docs/guide/basic-types.md
@@ -0,0 +1,59 @@
+Strings can be written in two ways: using double quotes (`"`), as we did above,
+or using single quotes (`'`). In both types of strings, you can escape a quote
+that otherwise would signify the end of the string by adding a single backslash
+before it. Consequently, backslashes also have to be escaped in the same way.
+The following are all valid strings:
+
+ "hello world"
+ 'hello world'
+ "it's a beautiful day"
+ 'it\'s a beautiful day'
+ "this is a backslash: \\"
+ "this is a double quote: \""
+ ""
+ ''
+
+Aside from strings, there are two more types of values that you can use when
+writing alv programs: numbers and booleans. Numbers use the digits 0-9 and
+can be integers, contain a decimal point, or start or end with a decimal point.
+Numbers can start with a negetive sign. The following are all valid numbers:
+
+ 0
+ 12
+ -7
+ 0.1
+ 10.
+ .1
+ 123.
+
+There are only two boolean values, `true` and `false`:
+
+ true
+ false
+
+The operator [print][], that we have been using above, only works on strings,
+but there is a similar operator called [trace][] that can be used to inspect
+any kind of value. It prints the value itself alongside more information, such
+as the values type. Give it a try:
+
+ (trace "hello")
+ (trace 2)
+ (trace true)
+
+This will print the following:
+
+ changes to files: values.alv
+ trace "hello": <str= "hello">
+ trace 2: <num= 2>
+ trace true: <bool= true>
+
+On the left side of the colon, [trace][] prints the expression that it is
+evaluating. On the right side, three pieces of information are shown:
+
+- the *type*: `str`, `num`, `bool`
+- the *value* itself: `"hello"`, `2`, `true`
+- the *kind* of the result: `=`
+
+`=` means that these values are *constant* - they will not change by themselves
+until the code is changed. For simple values like these that seems obvious, but
+in `alv` we can also create values tha change over time, as we will see soon.
diff --git a/docs/guide/copilot-gui.png b/docs/guide/copilot-gui.png
new file mode 100644
index 0000000..e0b3e25
--- /dev/null
+++ b/docs/guide/copilot-gui.png
Binary files differ
diff --git a/docs/guide/defining-symbols.md b/docs/guide/defining-symbols.md
new file mode 100644
index 0000000..5fb6d3f
--- /dev/null
+++ b/docs/guide/defining-symbols.md
@@ -0,0 +1,37 @@
+Another element of code in `alv` that we haven't discussed in detail yet are
+*symbols*. *Symbols* (like `trace`, `import*` or `math/+`) are names that serve
+as placeholders for previously *defined* values. When code is evaluated, symbols
+are looked up in the current *scope* and replaced with the corresponding value
+found there.
+
+When an `alv` file starts running, a number of symbols are defined in the
+default scope: These are the *builtins* mentioned above, and of which we have
+already been using [trace][], [import][], and [import*][].
+
+To *define a symbol* yourself, the [def][] builtin is used. It takes the symbol
+as its first, and the value to associate as its second parameter. After a symbol
+is defined, the name becomes an alias that behaves like the value itself. For
+example, we can use [def][] to associate the result of our calculation with the
+symbol `result`, and then refer to it by that symbol in the [trace][] operator:
+
+ (import* math)
+
+ (def result (+ 1 2))
+ (trace result)
+
+Symbols need to start with a letter or one of the characters `-_+*/.=~!?%`.
+After the first character, numbers are also allowed. There are two types of
+symbols that are treated specially: symbols containing a slash (`math/+`), and
+symbols starting and ending with asterisks (`*clock*`):
+
+- Symbols containing slashes (except at beginning and end of the symbol) are
+ split into multiple symbols, and looked up recursively in the scope. For
+ example, `math/+` is found by first looking for a value for the symbol `math`,
+ and then looking for the symbol `+` in that value. If the value for the
+ symbol `math` is not a scope, an error is thrown.
+- Symbols starting and ending with asterisks are called `dynamic symbols` and
+ are looked up in a different way inside user-defined functions. This will be
+ covered in detail later.
+- The two special formats can be mixed: when evaluating `*hello*/world`,
+ `alv` will look for the symbol `world` within the scope found by dynamically
+ resolving `*hello*`.
diff --git a/docs/guide/evaltime-and-runtime.md b/docs/guide/evaltime-and-runtime.md
new file mode 100644
index 0000000..e718cf3
--- /dev/null
+++ b/docs/guide/evaltime-and-runtime.md
@@ -0,0 +1,45 @@
+So far, `alv` may seem a lot like any other programming language - you write
+some code, save the file, and it runs, printing some output. "What about the
+'continuously running' aspect from the introduction?", you may ask yourself.
+
+So far, we have only seen *evaltime* execution in alv - but there is also
+*runtime* behavior. At *evaltime*, that is whenever there is change to the
+source code, `alv` behaves similar to a Lisp. This is the part we have seen
+so far. But once one such *eval cycle* has executed, *runtime* starts, and
+`alv` behaves like a dataflow system like [PureData][pd], [Max/MSP][max] or
+[vvvv][vvvv].
+
+What looked so far like static constants are actually *streams* of values.
+Whenever an input to an operator changes, the operator (may) update and respond
+with a change to its output as well. To see this in action, we need to start
+with a changing value. Number literals like `1` and `2`, which we used so far,
+are *evaltime constant*, which means simply that they will never update. Since
+all inputs to our [math/+][] operator are *evaltime constant*, the result is
+constant as well. To get some *runtime* activity, we have to introduce a
+side-effect input from somewhere outside the system.
+
+The [time/][] module contains a number of operators whose outputs update
+over time. Lets take a look at [time/tick][]:
+
+ (import* time)
+ (trace (tick 1))
+
+This will print a series of numbers, incrementing by 1 every second. The
+parameter to [time/tick][] controls how quickly it counts - try changing it to
+`0.5` or `2`. As you can see, we can change [time/tick][] *while it is
+running*, but it doesn't lose track of where it was!
+
+All of the other things we learned above apply to streams of values as well -
+we can use [def][] to store them in the scope, transform them using the ops
+from the [math/][] module and so on:
+
+ (import* time math)
+ (def tik (tick 0.25))
+ (trace (/ tik 4))
+
+Note that if you leave the [time/tick][]'s *tag* in place when you move it into
+the [def][] expression, it will keep on running steadily even then.
+
+[pd]: http://puredata.info/
+[max]: https://cycling74.com/products/max
+[vvvv]: https://vvvv.org/
diff --git a/docs/guide/functions.md b/docs/guide/functions.md
new file mode 100644
index 0000000..917b8d4
--- /dev/null
+++ b/docs/guide/functions.md
@@ -0,0 +1,54 @@
+Another builtin that creates a nested scope is [fn][], which is used to
+create a *user-defined function*, which can be used to simplify repetitive
+code, amongst other things:
+
+ (import* math)
+
+ (def add-and-trace
+ (fn
+ (a b)
+ (trace (+ a b))))
+
+ (add-and-trace 1 2)
+ (add-and-trace 3 4)
+
+Here a *function* `add-and-trace` is defined. When defining a function, first
+the names of the parameters have to be given. The function defined here takes
+two parameters, `a` and `b`. The last part of the function definition is called
+the *function body*.
+
+A function created using [fn][] can be called just like an operator. When a
+function is called, the parameters to the function are defined with the names
+given in the definition, and then the function body is executed. The previous
+example is equivalent to the following:
+
+ (import* math)
+
+ (def add-and-trace
+ (fn
+ (a b)
+ (trace (+ a b)))
+
+ (do
+ (let a 1
+ b 2)
+ (trace (+ a b)))
+
+ (do
+ (let a 3
+ b 4)
+ (trace (+ a b)))
+
+and the output of both is:
+
+ trace (+ a b): <num= 3>
+ trace (+ a b): <num= 7>
+
+In `alv`, functions are first-class values and can be passed around just like
+numbers, strings, etc. However it is very common to define a function with a
+name, so there is the `defn` shorthand, which combines the `def` and `fn`
+builtins into a single expression. Compare this equivalent definition of the
+`add-and-trace` function:
+
+ (defn add-and-trace (a b)
+ (trace (+ a b)))
diff --git a/docs/guide/getting-started-guide.md b/docs/guide/getting-started-guide.md
new file mode 100644
index 0000000..9ca2f63
--- /dev/null
+++ b/docs/guide/getting-started-guide.md
@@ -0,0 +1,25 @@
+`alv` ("alive") is a language for creating and changing realtime programs while
+they are running continuously. It can be used to create music, visuals or
+installations, but by itself creates neither sound nor video. Rather, `alv` is
+used together with other tools and synthesizers (for example
+[SuperCollider][supercollider] or [Pilot][pilot]). In such an ensemble of
+tools, `alive` takes the role of a 'conductor', telling the other tools what to
+play when by sending commands to them using a variety of protocols, such as OSC
+and MIDI.
+
+## contents
+
+1. [installation](installation.html)
+2. [hello world](hello-world.html)
+3. [working with the copilot](working-with-the-copilot.html)
+4. [syntax](syntax.html)
+5. [basic types](basic-types.html)
+6. [importing operators](importing-operators.html)
+7. [defining symbols](symbols.html)
+8. [scopes](scopes.html)
+9. [functions](functions.html)
+10. [evaltime and runtime](evaltime-and-runtime.html)
+11. [making sound](making-sound.html)
+
+[supercollider]: https://supercollider.github.io/
+[pilot]: https://github.com/hundredrabbits/Pilot
diff --git a/docs/guide/hello-world.md b/docs/guide/hello-world.md
new file mode 100644
index 0000000..7f1517d
--- /dev/null
+++ b/docs/guide/hello-world.md
@@ -0,0 +1,33 @@
+Before getting into all the details of the `alv` language, let's quickly run an
+example program to make sure that everything is working correctly:
+
+ ([1]import* time)
+ ([2]print ([3]every 0.5 "hello world!"))
+
+Open a text file save this piece of code as `hello.alv`. You can also find this
+example program in the repository and the windows binary package.
+
+As mentioned earlier, there are two different ways to run `alv` programs: using
+the copilot GUI, or in the terminal.
+
+## starting the copilot GUI
+On Linux and Mac OS X, you can launch the GUI by executing the `alv-fltk`
+command. On Windows, you can double-click `alv-fltk.bat`. This window should
+open:
+
+![a screeshot of the copilot GUI](copilot-gui.png)
+
+Now open `hello.alv` using `File > Open Script` or the shortcut `^O`
+(control-O). The copilot should start printing `hello world` over and over
+again in the lower field.
+
+You can pause and resume execution using the `Run` button or the `^P` shortcut.
+To stop the program simply close the window.
+
+## starting the copilot in the terminal
+To run a file in the terminal, invoke the command `alv <path/to/hello.alv>`. If
+your system cannot find the `alv` command, check your installation and `PATH`.
+On Windows, it is also possible to drag your `alv` file onto `alv.bat`.
+
+You should now see the text `hello world` being printed over and over again,
+twice a second. You can stop the copilot by pressing `^C` (control-C).
diff --git a/docs/guide/importing-operators.md b/docs/guide/importing-operators.md
new file mode 100644
index 0000000..4321e7a
--- /dev/null
+++ b/docs/guide/importing-operators.md
@@ -0,0 +1,26 @@
+Apart from [trace][], there are only very little builtin operators in `alv` -
+you can see all of them in the *builtins* section of the [reference][:/:].
+All of the 'real' functionality of `alv` is grouped into *modules*, that have
+to be loaded individually. *Modules* help organize all of the operators so that
+it is less overwhelming to look for a concrete feature. It is also possible to
+create your own plugins as new modules, which will be covered in another guide
+soon.
+
+Let's try using the [`+` operator][:math/+:] from the [math/][] module. To use
+operators from a module, we need to tell `alv` to load it first: We can load
+*all* the operators from the [math/][] module into the current scope using the
+[import*][] builtin:
+
+ (import* math)
+ (trace (+ 1 2))
+
+prints
+
+ trace (+ 1 2): <num= 3>
+
+Because it can get a bit confusing when all imported operators are mixed in the
+global scope, it is also possible to load the module into its own scope and use
+it with a prefix. This is what the [import][] builtin is for:
+
+ (import math)
+ (trace (math/+ 1 2))
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
new file mode 100644
index 0000000..5a192fa
--- /dev/null
+++ b/docs/guide/installation.md
@@ -0,0 +1,53 @@
+`alv` is written in the Lua programming language, and is compatible with both
+Lua 5.3 and luajit.
+
+## unix/linux and mac os
+Your distribution should provide you with packages for Lua and Luarocks. On Mac
+OS X, both are provided through [homebrew][homebrew]. After installing both of
+these, you should be able to start the Lua interpreter from the shell:
+
+ $ lua
+ Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
+ >
+
+You can exit using `CTRL+C`. If the version you see is not 5.3, double check
+your distribution packages or see if it was installed as `lua5.3` or `lua53`.
+Similarily, you should be able to run `luarocks`, `luarocks53` or `luarocks5.3`:
+
+ $ luarocks list
+
+ Rocks installed for Lua 5.3
+ ---------------------------
+
+Again, double check your installation or try adding `--lua-version 5.3` if the
+displayed version is not 5.3.
+
+With everything ready to go, you can now install `alv`:
+
+ $ luarocks install alive
+
+To use the copilot GUI, you will also need the `fltk4lua` package, which requires
+installing or building FLTK (also available through homebrew).
+
+ $ luarocks install fltk4lua
+
+With the `alive` package, two binaries should have been installed on your system:
+`alv` and `alv-fltk`. If you do not find these in your `$PATH`, you may need to
+apply the exports from `luarocks path` upon login, e.g. in your `.bashrc`.
+
+## windows
+For Windows, a binary package is available from the latest
+[github release][:*release*:]. It includes not only the `alv` source code, but
+also a compiled version of Lua 5.3 as well as Luarocks and all of `alv`'s
+dependencies.
+
+To use the binary package, simply extract the archive and move the folder
+wherever you want. You can now start the `hello.alv` example script by dragging
+it onto the `alv.bat` or `alv-fltk.bat` file in the folder.
+
+If you are going to use the command-line `alv.bat`, it is recommended to add
+the directory containing it to `%PATH%`, so that you can use the `alv` command
+anywhere on your system.
+
+[homebrew]: https://brew.sh
+[luarocks]: https://github.com/luarocks/luarocks/#installing
diff --git a/docs/guide/making-sound.md b/docs/guide/making-sound.md
new file mode 100644
index 0000000..1987dac
--- /dev/null
+++ b/docs/guide/making-sound.md
@@ -0,0 +1,58 @@
+As mentioned earlier, `alv` doesn't produce sound by itself. Instead, it is
+paired with other tools, and takes the role of a 'Conductor', sending commands
+and sequencing other tools.
+
+For the sake of this guide, we will be controlling [Pilot][pilot], a simple
+UDP-controlled synthesizer. You can go ahead and download and open it now.
+You should see a small window with a bunch of cryptic symbols and a little
+command line at the bottom. To verify that everything is working so far,
+try typing in `84c` and hitting enter. This should play a short sound (the note
+4C, played by the 8th default synthesizer voice in Pilot).
+
+To talk to Pilot from `alv`, we will use the [pilot/][] module. Note that for
+this module to work, you have to have the `osc` and `luasocket` dependencies
+installed. To play the same sound we played by entering `84c` above every 0.5
+seconds, we can use [time/every][] to send a `bang` to [pilot/play][]:
+
+ (import* time)
+ (import pilot)
+ (pilot/play (every 0.5) 8 4 'c')
+
+You can play with the voice, octave and note values a bit. To add a simple
+melody, we can use [util/switch][], which will cycle through a list of
+parameters when used together with [time/tick][]:
+
+ (import* time util)
+ (import pilot)
+ (pilot/play (every 0.5) 8 4
+ (switch (tick 0.5) 'c' 'd' 'a' 'f'))
+
+Now we can have the voice change every other loop as well:
+
+ (import* time util)
+ (import pilot)
+ (pilot/play (every 0.5)
+ (switch (tick 4) 8 9)
+ 4 (switch (tick 0.5) 'c' 'd' 'a' 'f'))
+
+To round off the sound a bit, we can turn on Pilot's reverb using
+[pilot/effect][]. Add the following somewhere in your file:
+
+ (pilot/effect "REV" 2 8)
+
+Now it's time to add some rhythm. The kick drum is voice 12 by default,
+and we can also add something like a snare on channel 3:
+
+ (pilot/play (every 0.75)
+ 12 2 'd' 3)
+ (pilot/play (every 2)
+ 13 4 'a' 4)
+
+Note that since we are using multiple individual [time/every][] instances,
+the timing of our voices relative to each other is not aligned - each voice
+started playing when the file was first saved with it added, and kept the
+rhythmn since. By deleting all their tags and re-saving the file, we can force
+`alv` to re-instantiate them all at the same time, thereby synchronising
+them.
+
+[pilot]: https://github.com/hundredrabbits/Pilot
diff --git a/docs/guide/scopes.md b/docs/guide/scopes.md
new file mode 100644
index 0000000..87568ef
--- /dev/null
+++ b/docs/guide/scopes.md
@@ -0,0 +1,82 @@
+Both [import][] and [import*][] are actually shorthands and what they
+accomplish can be done using the lower-level builtins [def][], [use][] and
+[require][]. Here is how you could replace [import][]:
+
+ #(with import:)
+ (import math)
+ (trace (math/+ 1 2))
+
+ #(with def and require:)
+ (def math (require "math"))
+ (trace (math/+ 1 2))
+
+[require][] returns a *scope*, which is defined as the symbol `math`.
+Then `math/+` is resolved by looking for `+` in this nested scope. Note that
+the symbol that the scope is defined as and the name of the module that is
+loaded do not have to be the same, you could call the alias whatever you want:
+
+ #(this not possible with import!)
+ (def fancy-math (require "math"))
+ (trace (fancy-math/+ 1 2))
+
+Most of the time the name of the module makes a handy prefix already, so
+[import][] can be used to save a bit of typing and make the code look a bit
+cleaner. [import*][], on the other hand, defines every symbol from the imported
+module individually. It could be implemented with [use][] like this:
+
+ (use (require "math"))
+ (trace (+ 1 2))
+
+[use][] copies all symbol definitions from the scope it is passed to the
+current scope.
+
+Note that [import][], [import*][], [def][], and [use][] all can take multiple
+arguments:
+
+ #(using the shorthands:)
+ (import* math logic)
+ (import midi osc)
+
+ #(using require, use and def:)
+ (use (require "math") (require "logic"))
+ (def midi (require "midi")
+ osc (require "osc"))
+
+It is common to have an [import][] and [import*][] expression at the top of an
+`alv` program to load all of the modules that will be used later, but the
+modules don't necessarily have to be loaded at the very beginning, as long as
+all symbols are defined before they are being used.
+
+## nested scopes
+Once a symbol is defined, it cannot be changed or removed:
+
+ (def a 3)
+ (def a 4) #(error!)
+
+It is, however, possible to 'shadow' a symbol by re-defining it in a nested
+scope: So far, all symbols we have defined - using `def`, [import][] and
+[import*][] - have been defined in the *global scope*, the scope that is active
+in the whole `alv` program. The [do][] builtin can be used to create a new
+scope and evaluate some expressions in it:
+
+ (import string)
+
+ (def a 1
+ b 2)
+
+ (trace (.. "first: " a " " b))
+ (do
+ (def a 3)
+ (trace (.. "second: " a " " b))
+ (trace (.. "third: " a " " b))
+
+This example prints the following:
+
+ trace (.. "first: " a " " b): <Value str: first: 1 2>
+ trace (.. "second: " a " " b): <Value str: second: 3 2>
+ trace (.. "third: " a " " b): <Value str: third: 1 2>
+
+As you can see, within a nested scope it is possible to overwrite a definition
+from the parent scope. Symbols that are not explicitly redefined in a nested
+scope keep their values, and changes in the nested scope do not impact the
+parent scope.
diff --git a/docs/guide/syntax.md b/docs/guide/syntax.md
new file mode 100644
index 0000000..93f1c61
--- /dev/null
+++ b/docs/guide/syntax.md
@@ -0,0 +1,43 @@
+`alv`'s syntax is very similar to Lisp. Expressions take the form of
+parenthesized lists like `(head a b c...)`, where the first element of the list
+(`head`) is the name of an operator or function, which defines what the
+expression as a whole will do, while the other elements are parameters whose
+meaning depends on the `head`. Let's start with a simple operator, [print][]:
+[print][] is used simply to print messages to the copilot console.
+
+## expressions
+Elements of an expression have to be separated by whitespace, but any type of
+and amount of whitespace is valid: feel free to use spaces, tabs, and newlines
+to format code to your liking. The following are all equal and valid examples:
+
+ (print "hello world")
+
+ (+ 1
+ 2
+ 3)
+
+ (print
+ "hello world")
+
+ ( print "hello world" )
+
+It is however recommended to follow the [clojure style guide][clojure-style] as
+much as it does apply to alv. All further examples in this guide will respect
+this guideline, so you might just pick it up simply by following this guide.
+
+## comments
+To annotate your code, you can use comments. In `alv`, comments begin with
+`#(` and end on a matching `)`. This way you can comment out a complete
+expression simply by adding a `#` character in front.
+
+ #(this is a comment)
+
+ #(this is a long,
+ multi-line comment,
+ (and it also has nested parentheses).
+ It ends after this sentence.)
+
+You can put comments anywhere in your program where whitespace is allowed and
+it will simply be ignored by `alv`.
+
+[clojure-style]: https://github.com/bbatsov/clojure-style-guide
diff --git a/docs/guide/working-with-the-copilot.md b/docs/guide/working-with-the-copilot.md
new file mode 100644
index 0000000..f40d82d
--- /dev/null
+++ b/docs/guide/working-with-the-copilot.md
@@ -0,0 +1,59 @@
+While it is possible to simply run finished programs as we just did with the
+`hello.alv` example, `alv` is a *livecoding language*, which means that it is
+designed so that programs can be written interactively while they are already
+running. To see how this works, let's re-write the `hello.alv` example
+step-by-step.
+
+First you will need an empty file to start from. Open a new file in your
+preferred text editor and save it as `test.alv`. Before adding any code, start
+the copilot (see the last page on the two ways of doing that).
+
+ $ alv test.alv
+ changes to files: test.alv
+
+You should see a note indicating that `alv` processed the file. The note will
+show up in the upper pane labelled `eval` (in the GUI), or colored green (in
+the terminal). This marks the message as an *eval-time message*, meaning that
+the message was printed as a direct response to the file changing or being
+loaded the first time. Other messages that might print to the `eval` section
+are things like errors in your program, and one-time debugging messages.
+
+Now that the copilot is running, whenever you change `test.alv` and save it, the
+copilot will reload it and execute your new code. When you are done, you can
+stop the copilot at any time by closing the GUI window or pressing `^C`
+(control-C) in the terminal.
+
+Let's start with a simple operator, [print][]: [print][] is used simply to print
+messages to the copilot console. Enter the following in your file and save it:
+
+ (print "hello world!")
+
+As soon as you save the file, you should notice two things happening:
+
+1. The copilot will print two new lines:
+
+ changes to files: hello.alv
+ hello world!
+
+ In the first line, it notifies us that the file has changed. In the second
+ line, you can see the output from the [print][] expression.
+2. The copilot will make a small modification to your file. Depending on the
+ editor you are using, this may either result in you seeing the modification
+ immediately, or a notice appearing that offers the option to reload the
+ file. If it is the latter, confirm the notification to accept the changes.
+ If there is an option to do so, you may want to configure your editor to
+ always reload the file automatically.
+
+The code should now look like this:
+
+ ([1]print "hello world")
+
+The `[1]` that the copilot added to your expression is that expression's `tag`.
+In `alv`, every expression has a tag that helps the copilot to identify the
+individual expressions as you make changes to your code. The copilot will make
+sure that all expressions are tagged by adding missing tags when you save the
+file, but you have to watch out not to duplicate a tag when copying and pasting
+code. When you duplicate code that already has tags, you can either manually
+change the tags to be unique, or simply delete the whole tag (including the
+square brackets) and let the copilot generate a new one for you the next time
+you save the file.