Source guide/modules.simd
1# Modules and Imports 2 3Silo organises code into **modules**: one per source file, 4grouped into **namespaces** by directory, collected into 5**packages** that sit under a top-level **namespace** name. 6Every declaration carries a visibility mode that controls who 7can see it; every file controls what it sees via a single 8`:use` block at the top. 9 10## Namespaces must have a slash 11 12External package paths follow the shape 13 14``` 15namespace/sub:package.module.submodule.Name 16``` 17 18The **namespace** part — everything before the `:` — **must 19contain at least one `/`**. This is how the compiler 20distinguishes an external import from an internal one: an 21internal module reference never has a `/` in it, while any 22external package reference always does. 23 24Valid external namespaces: 25 26``` 27foo-org/foo # a namespace with one slash 28deep/ns/path # deeper nesting is fine 29``` 30 31Invalid: 32 33``` 34foo # no slash: not a valid external namespace 35``` 36 37There is **one reserved exception**: `silo`. The `silo` 38namespace is reserved for the language itself. `silo:std` is 39the standard library; `silo:lang` is a conventional place for 40language-built-in items. No other no-slash namespace is 41allowed. 42 43Putting the pieces together, a fully-qualified external path 44looks like: 45 46``` 47silo:std.iter.CharsIter # namespace "silo", package "std", 48 # module "iter", name "CharsIter" 49foo-org/foo:types.User # namespace "foo-org/foo", 50 # package "types", name "User" 51deep/ns/path:pkg.mod.Thing # multi-segment namespace 52``` 53 54Within a package you use short paths — just the module chain 55from the package root. The `/` rule is specifically for 56referring to *other* packages. 57 58## Distinctive features 59 60Aside from the namespace rule, the module system's parts worth 61knowing up front are the lack of an orphan rule on trait 62impls, the way imports are explicitly scoped rather than 63globally visible, and the `sealed` visibility that lets a 64declarer allow use without allowing implementation. The rest 65of the chapter walks through the mechanics. 66 67## One file, one module 68 69A `.si` file is a module. Directory structure forms namespaces. 70If a package has: 71 72``` 73src/ 74├── lib.si # package root 75├── geometry.si # module `geometry` 76├── math/ 77│ ├── mod.si # module `math` 78│ └── trig.si # module `math.trig` 79└── net/ 80 ├── mod.si # module `net` 81 └── http.si # module `net.http` 82``` 83 84then the qualified path `math.trig.sin` refers to the `sin` word 85in `src/math/trig.si`. The dots in the path are namespace 86separators. 87 88Fully qualified external paths additionally include the package 89name: `silo:std.iter.CharsIter` refers to the `CharsIter` type 90in the `iter` module of the `std` package in the `silo` 91namespace. See [the standard-library guide's 92`silo:std.lib`](./mod.simd) for the canonical shape. 93 94Within a module, declaration order doesn't matter — every 95declaration is visible throughout the file. Circular 96dependencies between modules are allowed too; the compiler 97resolves them at link time. 98 99## The `:use` block 100 101Imports live in a `:use` block at the top of the file. Paths 102inside a `:use` are always qualified the same way external 103references are elsewhere in the language — with the namespace 104prefix for other packages, or with the module chain for things 105inside the current package: 106 107```silo 108:use 109 :open silo:std.collections Vec HashMap 110 :open silo:std.traits Eq Ord 111 :open foo-org/geometry:shapes Point distance 112 :open my-app.internal.helpers Logger 113 :open silo:std.iter Iterator => Iter # aliased 114 :impl foo-org/logging:adapters (Display Point) # orphan impl 115 :aspect foo-org/telemetry:tracing Trace # aspect 116:end 117``` 118 119Four import directives: 120 121- **`:open <module-path> <names...>`** — bring specific words 122 / types into scope from another module. The arrow form 123 `Iterator => Iter` aliases the import locally (useful when 124 two modules export the same name or you want a shorter 125 handle). 126- **`:impl <package-path> (Trait Type)`** — bring an **orphan 127 impl** into scope. See below. 128- **`:aspect <package-path> Aspect`** — bring an aspect into 129 scope so its operations are callable without qualification. 130 131Every name a `:use` block brings in must be explicit — there 132is no glob import. If you need twenty things from a module you 133name twenty things, or you reach for an **aggregation module** 134that re-exports them in one place using `:open(pub)`, and then 135`:open` from the aggregation module instead. 136 137The one exception to the explicit-import rule is 138`silo:std.prelude`: it is **automatically imported into every 139module** (`si[module.prelude+4]`), so the common stdlib types 140and traits are always in scope without an explicit `:use`. The 141prelude itself is an ordinary module — the only special thing 142about it is the automatic import. It is not a mechanism 143packages can opt into for their own preludes; your own 144aggregation modules still need an explicit `:use` at each 145consumer. 146 147## Visibility modes 148 149Every top-level declaration has a visibility, given in parens 150after the keyword: 151 152| Mode | Meaning | 153|------------|------------------------------------------------------| 154| `mod` | Module-private. The default. | 155| `pkg` | Visible within the same package. | 156| `pub` | Visible everywhere. | 157| `sealed` | Visible everywhere, but **impl-restricted to the declaring package**. | 158 159Examples: 160 161```silo 162:fn(pub) public-api ( -> Int ) 42 :end 163:fn(pkg) internal-helper ( -> Int ) 7 :end 164:fn private-detail ( -> Int ) 3 :end # defaults to mod 165``` 166 167### `sealed` 168 169`sealed` is the one mode that doesn't just hide things. It 170applies to traits, effects, and aspects, and means "anyone can 171*use* me, but only my package can *implement* me." 172 173```silo 174:trait(sealed) Error self { (Display self) } 175 .message ( self -> Str ) "{}" format ; 176 .source ( self -> (Option Error) ) drop None ; 177:end 178``` 179 180In the same package, impls of `Error` are fine: 181 182```silo 183:impl Error MyError 184 .message ( MyError -> Str ) .msg ; 185:end 186``` 187 188In a different package, the compiler rejects the attempt: 189 190```silo 191# In some other package: 192:impl Error ForeignType ... # compile error: Error is sealed 193``` 194 195Same story for `:effect(sealed)` — callers can constrain on the 196effect, but only the declaring package can `:with` a handler. 197Useful for host-level effects where the host is the only valid 198implementer. 199 200## No orphan rule 201 202**Any package can implement any trait on any type** — including 203standard-library traits on your own types, and your own traits 204on standard-library types, and third-party traits on fourth- 205party types. Coherence is handled per-import-scope rather than 206per-program: an impl is only *in scope* where an `:impl` import 207brings it in. 208 209Rust forbids this — you can only implement a trait for a type 210if you own one of them — because Rust's resolution is global 211and overlapping impls from different crates would break that 212global view. Silo resolves impls from the import scope at the 213call site, so "crate A's impl of (Display T)" and "crate B's 214impl of (Display T)" can coexist in the world as long as no 215single file tries to bring both in at once. 216 217- **Automatic scope**: an impl defined in the same package as 218 the trait, or in the same package as the type, is in scope 219 whenever the trait or the type is imported. That covers the 220 common case and you rarely need explicit imports. 221- **Orphan impls**: impls defined in a package that owns 222 neither the trait nor the type must be brought in via 223 `:impl package (Trait Type)` in the `:use` block. 224 225```silo 226:use 227 :open foo-org/geometry:shapes Point 228 :impl bar-org/logging:adapters (Display Point) 229 # Display from silo:std, Point from foo-org/geometry, 230 # impl defined in bar-org/logging — orphan, explicit import 231:end 232``` 233 234If two orphan impls of the same `(Trait Type)` pair both try to 235be in scope at the same call site, it's a compile error and the 236importer disambiguates by dropping one of the `:impl` imports. 237:gloss[Specialisation](./A1-glossary.simd#specialisation) handles the 238cases where one impl is strictly more specific than another. 239 240The benefit: third-party glue code is possible. You can add 241`Display` to someone else's type, add a custom `From` between 242two types you didn't write, without "newtype wrappers" 243everywhere. The cost: importers sometimes have to say which 244orphan impls they want. 245 246## Sealed vs orphan 247 248The two mechanisms address different concerns: 249 250- `sealed` is **about the author**: "I don't want anyone 251 implementing this trait because I need invariants." The author 252 restricts downstream behaviour. 253- No orphan rule + scoped impls is **about the ecosystem**: 254 "anyone can add impls but coherence is per-import-scope." The 255 ecosystem gets mix-and-match flexibility. 256 257Use `sealed` for traits where implementer control matters 258(error taxonomies, capability traits). Default to non-sealed for 259everything else. 260 261## Preludes 262 263The standard library has a prelude module (`silo:std.prelude`) 264that re-exports the commonly-used names: 265 266- Core types (`Int`, `Str`, `Bool`, `Vec`, `HashMap`, etc.). 267- Common traits (`Eq`, `Ord`, `Hash`, `Display`, `Debug`, 268 `From`, `Into`, `Foldable`, `Iterator`, …). 269- Common aspects (`CurrentLocale`, tracing, logging where 270 appropriate). 271 272Every package auto-imports the prelude. Your own package can 273define its own prelude module that extends or overrides the 274standard one for internal code. 275 276## Key points 277 278- One module per `.si` file; directories form namespaces. 279 `mod.si` under a directory is the directory's own module. 280- Imports go in a single `:use` block at the top of the file: 281 `:open`, `:impl`, `:aspect` directives. 282- Visibility modes: `mod` (default, module-private), `pkg`, 283 `pub`, `sealed`. `sealed` restricts **implementation**, not 284 use. 285- No orphan rule. Instead, `:impl package (Trait Type)` imports 286 orphan impls explicitly, and automatic scope covers the 287 common case. 288- :gloss[Specialisation](./A1-glossary.simd#specialisation) breaks 289 ties when multiple impls match; ambiguous cases are compile 290 errors. 291- External paths are `namespace:package.module.Name`; the 292 namespace part **must contain a `/`** (`foo-org/foo`, 293 `deep/ns/path`) except for the reserved `silo` namespace. 294 Internal paths drop the `namespace:package` prefix. 295 296Next: [lifecycle](./17-lifecycle.simd) — locality modes, 297`:dispose`, and how Silo handles memory without a garbage 298collector.