From 4b8a0f024606ca8c602a4e886767d414995ca0a2 Mon Sep 17 00:00:00 2001 From: s-ol Date: Wed, 3 Jun 2020 12:50:20 +0200 Subject: split guide into guide and reference --- docs/.gitignore | 1 - docs/gen/index | 43 +++++----------- docs/gen/layout.moon | 42 +++++++++++++--- docs/gen/module | 16 +++--- docs/guide/01_installation.md | 53 ++++++++++++++++++++ docs/guide/02_hello-world.md | 33 +++++++++++++ docs/guide/03_working-with-the-copilot.md | 59 ++++++++++++++++++++++ docs/guide/04_syntax.md | 43 ++++++++++++++++ docs/guide/05_basic-types.md | 59 ++++++++++++++++++++++ docs/guide/06_importing-operators.md | 26 ++++++++++ docs/guide/07_defining-symbols.md | 37 ++++++++++++++ docs/guide/08_making-sound.md | 58 ++++++++++++++++++++++ docs/guide/basic-types.md | 59 ---------------------- docs/guide/defining-symbols.md | 37 -------------- docs/guide/evaltime-and-runtime.md | 45 ----------------- docs/guide/functions.md | 54 -------------------- docs/guide/getting-started-guide.md | 25 ---------- docs/guide/hello-world.md | 33 ------------- docs/guide/importing-operators.md | 26 ---------- docs/guide/index.md | 27 ++++++++++ docs/guide/installation.md | 53 -------------------- docs/guide/making-sound.md | 58 ---------------------- docs/guide/scopes.md | 82 ------------------------------- docs/guide/syntax.md | 43 ---------------- docs/guide/working-with-the-copilot.md | 59 ---------------------- docs/index.md | 4 +- docs/reference/02_evaltime-and-runtime.md | 45 +++++++++++++++++ docs/reference/03-1_symbol-resolution.md | 82 +++++++++++++++++++++++++++++++ docs/reference/03-3_functions.md | 54 ++++++++++++++++++++ docs/reference/index.md | 26 ++++++++++ docs/style.css | 3 +- 31 files changed, 661 insertions(+), 624 deletions(-) create mode 100644 docs/guide/01_installation.md create mode 100644 docs/guide/02_hello-world.md create mode 100644 docs/guide/03_working-with-the-copilot.md create mode 100644 docs/guide/04_syntax.md create mode 100644 docs/guide/05_basic-types.md create mode 100644 docs/guide/06_importing-operators.md create mode 100644 docs/guide/07_defining-symbols.md create mode 100644 docs/guide/08_making-sound.md delete mode 100644 docs/guide/basic-types.md delete mode 100644 docs/guide/defining-symbols.md delete mode 100644 docs/guide/evaltime-and-runtime.md delete mode 100644 docs/guide/functions.md delete mode 100644 docs/guide/getting-started-guide.md delete mode 100644 docs/guide/hello-world.md delete mode 100644 docs/guide/importing-operators.md create mode 100644 docs/guide/index.md delete mode 100644 docs/guide/installation.md delete mode 100644 docs/guide/making-sound.md delete mode 100644 docs/guide/scopes.md delete mode 100644 docs/guide/syntax.md delete mode 100644 docs/guide/working-with-the-copilot.md create mode 100644 docs/reference/02_evaltime-and-runtime.md create mode 100644 docs/reference/03-1_symbol-resolution.md create mode 100644 docs/reference/03-3_functions.md create mode 100644 docs/reference/index.md (limited to 'docs') diff --git a/docs/.gitignore b/docs/.gitignore index 9aad8c7..189bd90 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,4 +1,3 @@ *.html ldoc.* internals/*.css -reference diff --git a/docs/gen/index b/docs/gen/index index dff601a..20fb3bd 100755 --- a/docs/gen/index +++ b/docs/gen/index @@ -1,8 +1,8 @@ #!/usr/bin/env moon require 'alv' -import render, layout, autoref from require 'docs.gen.layout' +import render, layout, autoref, subnav from require 'docs.gen.layout' import section, h1, h2, p, ul, li, a, code from require 'docs.gen.dom' -import opairs from require 'alv.util' +import compile from require 'discount' export OUT @@ -16,34 +16,15 @@ spit = (file, str) -> file\write str file\close! -{ OUT } = arg +{ OUT, file } = arg +md = slurp file +for file in *arg[3,] + if mod = file\match '/module/(.*)%.html' + name = mod\gsub '/', '.' + md ..= " - [`#{name}`](module/#{mod}.html)\n" + +content = compile (autoref md), 'githubtags', 'fencedcode' +sub = subnav [a for a in *arg[3,]] spit OUT, layout - title: 'reference' - body: { - h1 (code 'alive'), " language reference" - p "This section documents all builtins and modules that are currently - available in the alive programming language." - p autoref "If you are new to alive, the [getting started guide][:../guide/:] is - the recommended place to start. If you are looking for - information on adding your own module or contributing to alive, check - out the [developer documentation][:../internals/index/:]." - section { - id: 'modules' - h2 a "module index", href: '#modules' - p autoref "These modules can be imported using [require][], " .. - "[import][] and [import*][]." - ul for file in *arg[2,] - path = file\match '^alv%-lib/(.*)%.moon$' - name = path\gsub '/', '.' - li a (code name), href: "#{path}.html" - } - section { - id: 'builtins' - h2 a "builtins", href: '#builtins' - p "These definitions are automatically loaded into the global Scope of - every alive session." - ul for key, val in opairs (require 'alv.builtins').values - li render key, val.result - } - } + body: table.concat { sub, content.body }, '\n\n' diff --git a/docs/gen/layout.moon b/docs/gen/layout.moon index 1b1cdaa..7d3c4e1 100644 --- a/docs/gen/layout.moon +++ b/docs/gen/layout.moon @@ -67,7 +67,10 @@ link = (ref) -> return version.release if ref == '*release*' mod, sym = ref\match '^(.+)/(.*)$' - abs "reference/#{mod or 'index'}.html##{sym or ref}" + if mod + abs "reference/module/#{mod}.html##{sym or ref}" + else + abs "reference/builtins.html##{sym or ref}" -- link to a reference r = (text, ref) -> @@ -88,10 +91,33 @@ autoref = (str) -> subnav = do split_name = (file) -> - href, label = file\match '^docs/(.*/([%w%-]+)%.html)' + if href = file\match '^docs/(.*/index.html)$' + return 'index', href + + href, label = file\match '^docs/(.*/([%d%w-_]+)%.html)$' + label = (label\match '^[%d-]+_([%w-]+)$') or label label = label\gsub '-', ' ' label, href + title = (file) -> + if file == 'docs/guide/index.html' + return "getting started guide" + if file == 'docs/reference/index.html' + return "alv language reference" + elseif mod = file\match '^docs/reference/module/(.*)%.html$' + return "#{mod} module reference" + elseif href = file\match '^docs/(.*/index.html)$' + error "index page without hardcoded name: #{href}" + + num, label = file\match '/([%d-]+)_([%w%-]+)%.html$' + if num + num = num\gsub '%f[%d]0', '' + num = num\gsub '-', '.' + label = label\gsub '-', ' ' + "#{num}. #{label}" + else + (file\match '/([%w%-]+)%.html$') + subnav_link = (dir, file) -> import span, a, u from dom @@ -119,7 +145,7 @@ subnav = do div { class: 'subheader' - h1 (split_name OUT) + h1 (title OUT) nav { subnav_link 'l', all[c-1] subnav_link 'r', all[c+1] @@ -129,7 +155,7 @@ subnav = do aopts = (href, pat) -> { href: abs href - class: if OUT\match "^docs/#{pat}" then 'active' + class: if OUT\match pat then 'active' } -- layout and write a doc page @@ -149,10 +175,10 @@ layout = (opts) -> ' documentation' } div class: 'grow' - a 'home', aopts 'index.html', 'index.html$' - a 'guide', aopts 'guide/getting-started-guide.html', 'guide' - a 'reference', aopts 'reference/index.html', 'reference' - a 'internals', aopts 'internals/index.html', 'ldoc' + a 'home', aopts 'index.html', '^docs/index.html$' + a 'guide', aopts 'guide/index.html', '^docs/guide' + a 'reference', aopts 'reference/index.html', '^docs/reference' + a 'internals', aopts 'internals/index.html', '^docs/ldoc' } body = article opts.body title = if opts.title diff --git a/docs/gen/module b/docs/gen/module index d726845..77336cf 100755 --- a/docs/gen/module +++ b/docs/gen/module @@ -1,7 +1,7 @@ #!/usr/bin/env moon -import Scope from require 'alv' -import render, layout from require 'docs.gen.layout' -import section, h2, h3, ul, li, code from require 'docs.gen.dom' +import Constant from require 'alv' +import render, layout, subnav from require 'docs.gen.layout' +import h2, h3, ul, li, code from require 'docs.gen.dom' import opairs from require 'alv.util' export OUT, require @@ -26,11 +26,13 @@ spit = (file, str) -> { OUT, module, name } = arg name or= module -module = Scope.from_table require module +const = Constant.wrap require module +module = const\unwrap! spit OUT, layout - title: "#{name} reference" - body: section { + title: "#{name} module reference" + body: table.concat { + subnav [a for a in *arg[4,]] h2 (code name), ' module reference' h3 'index' ul for key, node in opairs module.values @@ -38,4 +40,4 @@ spit OUT, layout h3 'details' ul for key, node in opairs module.values li render key, node.result - } + }, '' diff --git a/docs/guide/01_installation.md b/docs/guide/01_installation.md new file mode 100644 index 0000000..5a192fa --- /dev/null +++ b/docs/guide/01_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/02_hello-world.md b/docs/guide/02_hello-world.md new file mode 100644 index 0000000..7f1517d --- /dev/null +++ b/docs/guide/02_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 `. 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/03_working-with-the-copilot.md b/docs/guide/03_working-with-the-copilot.md new file mode 100644 index 0000000..f40d82d --- /dev/null +++ b/docs/guide/03_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. diff --git a/docs/guide/04_syntax.md b/docs/guide/04_syntax.md new file mode 100644 index 0000000..93f1c61 --- /dev/null +++ b/docs/guide/04_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/05_basic-types.md b/docs/guide/05_basic-types.md new file mode 100644 index 0000000..c22929c --- /dev/null +++ b/docs/guide/05_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": + trace 2: + trace 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/06_importing-operators.md b/docs/guide/06_importing-operators.md new file mode 100644 index 0000000..4321e7a --- /dev/null +++ b/docs/guide/06_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): + +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/07_defining-symbols.md b/docs/guide/07_defining-symbols.md new file mode 100644 index 0000000..5fb6d3f --- /dev/null +++ b/docs/guide/07_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/08_making-sound.md b/docs/guide/08_making-sound.md new file mode 100644 index 0000000..1987dac --- /dev/null +++ b/docs/guide/08_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/basic-types.md b/docs/guide/basic-types.md deleted file mode 100644 index c22929c..0000000 --- a/docs/guide/basic-types.md +++ /dev/null @@ -1,59 +0,0 @@ -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": - trace 2: - trace 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/defining-symbols.md b/docs/guide/defining-symbols.md deleted file mode 100644 index 5fb6d3f..0000000 --- a/docs/guide/defining-symbols.md +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index e718cf3..0000000 --- a/docs/guide/evaltime-and-runtime.md +++ /dev/null @@ -1,45 +0,0 @@ -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 deleted file mode 100644 index 917b8d4..0000000 --- a/docs/guide/functions.md +++ /dev/null @@ -1,54 +0,0 @@ -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): - trace (+ a b): - -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 deleted file mode 100644 index 9ca2f63..0000000 --- a/docs/guide/getting-started-guide.md +++ /dev/null @@ -1,25 +0,0 @@ -`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 deleted file mode 100644 index 7f1517d..0000000 --- a/docs/guide/hello-world.md +++ /dev/null @@ -1,33 +0,0 @@ -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 `. 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 deleted file mode 100644 index 4321e7a..0000000 --- a/docs/guide/importing-operators.md +++ /dev/null @@ -1,26 +0,0 @@ -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): - -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/index.md b/docs/guide/index.md new file mode 100644 index 0000000..6933717 --- /dev/null +++ b/docs/guide/index.md @@ -0,0 +1,27 @@ +`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. + +This guide will get you up and running with `alv` and [Pilot][pilot], explaining +most of the basics along the way. For more complete documentation on the `alv` +language and features like functions, loops, etc. take a look at the +[language reference](../reference/index.html). + +## contents + +1. [installation](01_installation.html) +2. [hello world](02_hello-world.html) +3. [working with the copilot](03_working-with-the-copilot.html) +4. [syntax](04_syntax.html) +5. [basic types](05_basic-types.html) +6. [importing operators](06_importing-operators.html) +7. [defining symbols](07_symbols.html) +8. [making sound](08_making-sound.html) + +[supercollider]: https://supercollider.github.io/ +[pilot]: https://github.com/hundredrabbits/Pilot diff --git a/docs/guide/installation.md b/docs/guide/installation.md deleted file mode 100644 index 5a192fa..0000000 --- a/docs/guide/installation.md +++ /dev/null @@ -1,53 +0,0 @@ -`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 deleted file mode 100644 index 1987dac..0000000 --- a/docs/guide/making-sound.md +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 87568ef..0000000 --- a/docs/guide/scopes.md +++ /dev/null @@ -1,82 +0,0 @@ -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): - trace (.. "second: " a " " b): - trace (.. "third: " a " " b): - -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 deleted file mode 100644 index 93f1c61..0000000 --- a/docs/guide/syntax.md +++ /dev/null @@ -1,43 +0,0 @@ -`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 deleted file mode 100644 index f40d82d..0000000 --- a/docs/guide/working-with-the-copilot.md +++ /dev/null @@ -1,59 +0,0 @@ -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. diff --git a/docs/index.md b/docs/index.md index cd79c99..08c7573 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,11 +20,11 @@ it to). [on github][:*repo*:], and is licensed under [GPLv3][license]. If you want to learn more or try out `alv` yourself, the -[getting started][guide] page is a good place to start. On the other hand, if +[getting started guide][guide] is a good place to start. On the other hand, if you are a curious about the motivations and concepts behind `alv`, you can find more in-depth information on these topics in the ['persistent expressions' article][rationale]. -[guide]: guide/getting-started-guide.html +[guide]: guide/index.html [rationale]: https://s-ol.nu/alivecoding [license]: https://github.com/s-ol/alive/blob/master/LICENSE diff --git a/docs/reference/02_evaltime-and-runtime.md b/docs/reference/02_evaltime-and-runtime.md new file mode 100644 index 0000000..e718cf3 --- /dev/null +++ b/docs/reference/02_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/reference/03-1_symbol-resolution.md b/docs/reference/03-1_symbol-resolution.md new file mode 100644 index 0000000..87568ef --- /dev/null +++ b/docs/reference/03-1_symbol-resolution.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): + trace (.. "second: " a " " b): + trace (.. "third: " a " " b): + +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/reference/03-3_functions.md b/docs/reference/03-3_functions.md new file mode 100644 index 0000000..917b8d4 --- /dev/null +++ b/docs/reference/03-3_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): + trace (+ a b): + +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/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..cfa8fdc --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,26 @@ +__This section is currently under construction.__ + +This reference manual documents the `alv` langauge and its standard facilities +in detail. If you are new to alive, the [getting started guide][guide] is the +recommended place to start. If you are looking for information on adding your +own module or contributing to alive, check out the +[developer documentation](../internals/index.html). + +[guide]: (../guide/index.html) + +## contents + +1. syntax +2. [evaltime and runtime](02_evaltime-and-runtime.html) +3. evaltime + 1. [symbol resolution](03-1_symbol-resolution.html) + 2. if and switch + 3. [functions](03-3_functions.html) + 4. dynamic symbols + 5. loops + 6. modules and loading +4. runtime + 1. result kinds + 2. pure operators +5. [builtin listing](builtins.html) +6. included modules diff --git a/docs/style.css b/docs/style.css index 51fddef..e3238e9 100644 --- a/docs/style.css +++ b/docs/style.css @@ -62,9 +62,10 @@ a { ul, ol { margin: 0.5em 0; - padding-left: 0.6em; + padding-left: 1em; } ul, .ldoc ol { list-style: '- '; } +li > ul, li > ol { margin: 0; } header { position: fixed; -- cgit v1.2.3