diff options
Diffstat (limited to 'documentation/book/the_lux_programming_language')
-rw-r--r-- | documentation/book/the_lux_programming_language/appendix_a.md | 247 | ||||
-rw-r--r-- | documentation/book/the_lux_programming_language/chapter_1.md | 1 |
2 files changed, 247 insertions, 1 deletions
diff --git a/documentation/book/the_lux_programming_language/appendix_a.md b/documentation/book/the_lux_programming_language/appendix_a.md new file mode 100644 index 000000000..29a84a9df --- /dev/null +++ b/documentation/book/the_lux_programming_language/appendix_a.md @@ -0,0 +1,247 @@ +# Appendix A: Import syntax + +You've already seen some import syntax, but now you'll see all the options available. + +If you recall [Chapter 1](chapter_1.md), there was this example code: + +``` +(.module: + [library + [lux #* + [program (#+ program:)] + ["." debug] + [control + ["." io]]]]) +``` + +Here, we're importing the `library/lux` module. +The `#*`/`#all` option means _locally import every definition exported by the `library/lux` module_. + +This allows usage of those definitions without having to give them the `library/lux.` prefix, or even the `.` shortcut prefix. + +This may cause some issues if you import 2 definitions with the same name from different modules; or if you get a definition from one module, but then write your own definition with the same name in your code. +In those circumstances, the compiler will complain, saying that you can't re-define `X`; where `X` is the name of the definition. + +Then, we import `library/lux/program`, but we **only** import locally the `program:` definition. +That is what the `#+`/`#only` option allows. + + There is also a `#-`/`#exclude` option which means locally import everything **except** the specified definitions_. + You could use it like this: `[program (#- foo bar baz)]` + +Finally, we import both the `library/lux/debug` and `library/lux/control/io` modules. +In neither case do we import any of their definitions locally. +We also give both of those modules local aliases. +That is what that `"."` syntax does. +The `.module:` macro recognizes that syntax for aliases and replaces the dot/period with the import name directly to the right. +That means: +* `"."` + `debug` = `debug` +* `"."` + `io` = `io` + +This might not seem like a big deal, but the aliasing syntax allows you to give imports arbitrary names, so long as you take into account the substitutions that may happen. + +So, for example: +* `"my_."` + `debug` = `my_debug` +* `"._."` + `io` = `io_io` + +It is also important to note that while imports can be nested for convenience, they don't have to be. + +The `.module:` declaration could just as easily been written like this: + +``` +(.module: + [library/lux #*] + [library/lux/program (#+ program:)] + ["debug" library/lux/debug] + ["io" library/lux/control/io]) +``` + +You might also guess that `library` was not imported as a module because it was neither given an alias, not had any definitions specified as local imports. +Any module-path fragments included in the import syntax without such options will not be imported and will simply be assumed to be part of the module-paths of the sub-modules specified under them. + +--- + +It is also possible to have the `.module:` macro open interface implementations for you when importing the modules that contain them. +For example: + +``` +(.module: + [library + [lux #* + [data + [collection + ["." list ("#::." functor monoid)]]]]]) +``` + +The import above would locally import: +* `list::map`, from `functor`. +* `list::identity`, from `monoid`. +* `list::compose`, from `monoid`. + +Here, we can also see some additional syntax for aliasing. +First of all, when opening implementations, aliasing syntax is used to determine the names of the local implementation imports. +The `.` is replaced with the name of the implementation member. +The `#` is bound to the name of the _context_ of the import. +In this case, the implementations are coming from the `library/lux/data/collection/list` module, so that is the context. +And since that module has been imported with the local alias `list`, that is the name that replaces the `#` in the aliasing syntax for the implementation imports. +And that is how we end up with the list of names I enumerated above. + +The `#` syntax for aliasing can also be used between modules, and not just when importing implementation members. + +For example: + +``` +(.module: + [library + [lux #* + [data + ["." collection #_ + ["#/." list ("#::." functor monoid)]]]]]) +``` + +Would locally import: +* `collection/list::map`, from `functor`. +* `collection/list::identity`, from `monoid`. +* `collection/list::compose`, from `monoid`. + +The context between module imports corresponds to the closest ancestor path which has itself been aliased. +Non-aliased paths don't count as context. + +This means: + +``` +(.module: + [library + [lux #* + ["." data #_ + [collection + ["#/." list ("#::." functor monoid)]]]]]) +``` + +Would locally import: +* `data/list::map`, from `functor`. +* `data/list::identity`, from `monoid`. +* `data/list::compose`, from `monoid`. + + Also, that `#_`/`#ignore` syntax you may have noticed means _do not import this module; just give it an alias I can refer to later as a context_. + +I should also note that you can **both** locally import definitions and open implementations as parts of the same module import. + +For example: + +``` +(.module: + [library + [lux #* + [data + [collection + ["." list (#+ repeated size) ("#::." monad)]]]]]) +``` + +--- + +Another important feature of module imports is relative addressing, which comes in 2 flavors. + +For the first one, suppose you have the following directory structure: + +``` +program + foo + bar + baz + quux +test + foo + bar + baz + quux +``` + +And you're writing code in the `program/foo/baz` module. + +You can import other modules in the hierarchy like this: + +``` +... In program/foo/baz +(.module: + [library + [lux #*]] + ["." /quux] ... program/foo/baz/quux, aliased as /quux + ["." //bar] ... program/foo/bar, aliased as //bar + ["." ///] ... program, aliased as /// + ) +``` + +A single forward slash (`/`) signifies _"this module"_ in the hierarchy, so anything after the forward slash is assumed to be under _this module_. +Two forward slashes (`//`) signify _"the module above"_, and any forward slash after that allows you to go further up the hierarchy. +In the case of `program`, it's enough to just specify three forward slashes (`///`) for the `.module:` macro to know which module you're referring to. + +You can think about it like this: + +* `program/foo/baz` + `/` + `quux` = `program/foo/baz/quux` +* `program/foo/baz` + `//` + `bar` = `program/foo` + `/` + `bar` = `program/foo/bar` +* `program/foo/baz` + `///` = `program/foo` + `//` = `program` + `/` = `program` + +Also, this relative path syntax can be nested, like so: + +``` +... In program/foo/baz +(.module: + [library + [lux #*]] + [/ + ["." quux]] ... program/foo/baz/quux, aliased as quux + [// + ["." bar] ... program/foo/bar, aliased as bar + ] + ["." ///] ... program, aliased as /// + ) +``` + +Or even: + +``` +... In program/foo/baz +(.module: + [library + [lux #*]] + [/ + ["." quux] ... program/foo/baz/quux, aliased as quux + [// + ["." bar] ... program/foo/bar, aliased as bar + ["program" //] ... program, aliased as program + ]]) +``` + +You may have noticed that when importing `program`, we went from `///` to `//`. +That is because, since it's nested under another `//`, it's relative to `program/foo` instead of `program/foo/baz`, so only 1 step up is necessary instead of the 2 steps a `///` would provide. + +--- + +For the second way to do relative imports, you can see this example: + +``` +... In program/foo/baz +(.module: + [library + [lux #*]] + [\\test + ["." /] ... test/foo/baz, aliased as / + ] + ... Alternatively + ["." \\test] ... test/foo/baz, aliased as \\test + ... Or + [\\ + [\test + ["." /] ... test/foo/baz, aliased as / + ]] + ) +``` + +The backslash (`\`) works in the reverse direction to the forward slash (`/`). +If the forward slash allows you append paths to the back, and to move up the hierarchy from the end; then the backslash allows you to append paths to the front, and the move down the hierarchy from the beginning. + +Why would you want such a thing? + +Because it allows you to easily establish parallel hierarchies of modules, which is a useful way to separate orthogonal aspects of your program (like the `program` and `test` hierarchies in our example). +Then, by using this relative syntax, you can refer to one hierarchy from another in an easy way. + diff --git a/documentation/book/the_lux_programming_language/chapter_1.md b/documentation/book/the_lux_programming_language/chapter_1.md index 89aab1d53..9b670ef5c 100644 --- a/documentation/book/the_lux_programming_language/chapter_1.md +++ b/documentation/book/the_lux_programming_language/chapter_1.md @@ -53,7 +53,6 @@ These are the steps: ``` (.module: - {#.doc "This will be our program's main module."} [library [lux #* [program (#+ program:)] |