I moduli e i file
Un file è un modulo, e il percorso che scrivi vuol dire la stessa cosa da qualunque punto.
Quando un programma usa più file, devi sapere dove il compilatore li cerca e come usare le dichiarazioni che contengono.
In Heroes ogni file è un modulo. use geom/point legge geom/point.hero a partire dalla cartella del file che compili. Il nome del modulo è l’ultima parte del percorso: le sue dichiarazioni si usano nella forma point.qualcosa.
I percorsi partono dalla radice del programma
Se esegui heroes run shapes/main.hero, la cartella shapes/ è la radice del programma. Tutti i percorsi use, in qualsiasi file, partono da quella cartella.
use geom/point
use geom/area
use render/ascii
use render/scale as near_scale
use scale
Il programma contiene sei moduli in tre cartelle. Ecco come un file usa un modulo nella stessa cartella:
use geom/point
function rect(a: point.Point, b: point.Point) -> i64
return point.span(from: a, to: b) * height(a: a, b: b)
area.hero e point.hero sono nella stessa cartella, ma area.hero indica comunque il percorso dalla radice. La dichiarazione use geom/point è quindi identica in main.hero, geom/area.hero e render/ascii.hero. Spostare il file che la contiene dentro il programma non cambia il modulo a cui si riferisce.
Non si può salire
Nessun .., nessuna barra iniziale, nessun modo di uscire dalla
radice. Se ci provi, il compilatore ti dice qual è la regola invece di
dirti solo che la sintassi è sbagliata:
$ heroes check shapes/main.hero
error[module_path_climbs]: a `use` path cannot climb — every `use` starts at the directory of the file you compile, so write the path from there: `use lex/token`, wherever the file that writes it sits
at shapes/geom/area.hero:8:5
|
8 | use ../geom/point
| ^
note: a module above that directory belongs to a different program: give this one its own root, or move the file in
Il programma può usare soltanto file dentro la propria radice. Per includere un file esterno, spostalo all’interno oppure scegli come file iniziale uno in una cartella che contenga tutti i moduli necessari.
Il nome è l’ultimo pezzo
use geom/point rende disponibile il nome point, ricavato dall’ultima parte del percorso. Le cartelle non entrano nel nome: non si usa geom_point né geom.point.
corner = point.Point(x: 0, y: 0)
far = point.Point(x: 4, y: 3)
print(area.rect(a: corner, b: far))
print(area.perimeter(a: corner, b: far))
print(ascii.bar(width: point.span(from: corner, to: far)))
print(ascii.frame(width: 3, height: 2))
Per usare una dichiarazione di un altro modulo devi specificare il nome del modulo, detto qualificatore. Un tipo Point del modulo point si scrive con quel prefisso. Allo stesso modo, point.span(...) indica una funzione del modulo identificato dalla dichiarazione use.
Due moduli possono finire con la stessa parola
scale.hero nella radice e render/scale.hero hanno entrambi il nome scale. Possono esistere nello stesso programma, ma un file che usa entrambi deve distinguerli:
$ heroes check shapes/main.hero
error[module_binding_taken]: this file already calls `render/scale` by the name `scale`, so `scale` cannot have it too — a file may not bind one name twice
at shapes/main.hero:23:5
|
23 | use scale
| ^^^^^
note: the two modules may both exist and both be used — `as` says what THIS file calls each one, and two files may name the same module differently
Per risolvere il conflitto, aggiungi la clausola as alla dichiarazione indicata dal compilatore:
print(scale.factor())
print(near_scale.factor())
use render/scale as near_scale assegna al modulo il nome near_scale solo in questo file. Un altro file può continuare a chiamarlo scale. Puoi quindi risolvere il conflitto senza modificare il modulo. L’esempio stampa 10 usando lo scale della radice e 3 usando quello in render/.
Evitare conflitti tra nomi
use geom/point dichiara il nome point nel file. Una variabile nello stesso file non può usare quel nome: nasconderebbe il modulo, come spiegato nel capitolo 7:
$ heroes check shapes/main.hero
error[shadowed_binding]: `point` is what this file calls the module `geom/point`, so a local may not take it — shadowing is an error here
at shapes/main.hero:26:5
|
26 | point = point.Point(x: 0, y: 0)
| ^^^^^
note: rename the module for this file with `use geom/point as `, which clears every collision in it at once, or rename this binding
Questa regola evita che point.x indichi il modulo in una funzione e una variabile in un’altra. Il messaggio suggerisce due possibilità: rinominare il modulo oppure rinominare la variabile.
Le regole di accesso ai moduli
Non esistono public e private: le dichiarazioni di un modulo sono accessibili a ogni file che lo usa. Il qualificatore è sempre obbligatorio. Il compilatore segnala gli import circolari sulla riga use. Non servono file di progetto o configurazione: le cartelle organizzano i moduli e il file passato al comando determina la radice.
Poi: Test e buchi, dove un programma non finito è comunque un programma di cui il compilatore ti parla. ← Prima: Valori fino in fondo
Esegui un programma tuo: bastano quattro comandi. Oppure prendi tutto il linguaggio in un file solo, e tutti i programmi già scritti.