aboutsummaryrefslogtreecommitdiff
path: root/documentation/book/the_lux_programming_language/appendix_a.md
blob: 29a84a9dfcd054c64539f8c2312efa8b14c871a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
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.