Modules
@import binds a module to a name, pub says what another module may see.
// The standard library, and the module system it sits behind.
//
// `@import` binds a module to a name. Two files may each declare a `helper`
// without colliding, and the 27 HTTP status types no longer occupy the global
// namespace -- which is what the module system was for.
const str = @import("std/str");
const array = @import("std/array");
const math = @import("std/math");
const io = @import("std/io");
const http = @import("std/http");
fn render(s: http.Status) str { return "500 server error"; }
fn render(s: http.Status4xx) str { return "400 bad request"; }
fn render(s: http.NotFound404) str { return "404 not found"; }
// Statically a `Status`, so which overload wins is read from the type id in
// the object's header.
fn serve(s: http.Status) void { print(render(s)); }
fn main() i64 {
// Strings. `==` compares contents, so a string built at run time equals a
// literal.
const greeting = str.concat("hello, ", "world");
print(greeting);
print_int(str.len(greeting));
print_bool(greeting == "hello, world");
print(str.substr(greeting, 7, str.len(greeting)));
// Splitting produces an array of strings, which `join` puts back.
const fields = str.split("id,name,email", ",");
print_int(array.len(fields));
print(str.join(fields, " | "));
// Arithmetic that is not an operator. `abs` is an overload set; `min` is
// one function over the abstract type `Number`.
print_int(math.abs(-7));
print_float(math.sqrt(2.0));
print_int(math.min(3, 7));
print_int(math.ipow(2, 10));
// I/O, where the library first has to fail: a fallible builtin returns a
// `!T`, caught like any other error.
print(io.read_file("/definitely/not/a/file") catch "could not read it");
serve(http.NotFound404);
serve(http.Forbidden403);
serve(http.ServerError500);
return 0;
}wsharp run examples/library.wshello, world
12
true
world
3
id | name | email
7
1.4142135623730951
3
1024
could not read it
404 not found
400 bad request
500 server errorImporting
@import binds a module to a name, and everything in it is reached through that
name:
const str = @import("std/str"); // a library module
const util = @import("./helper.ws"); // a file next to this one
const json = @import("acme/json"); // a package this project depends onLibrary paths and package paths are extensionless. A relative path names a file and keeps its extension.
The name is yours to choose. const text = @import("std/str"); is fine, and the
example programs use whichever name reads best at the call site.
Two files may each declare a helper without colliding, and the 27 HTTP status
types no longer occupy the global namespace, which is what the module system was
for.
Visibility
Everything in a module is private to it unless it says pub:
pub const State = struct { total: i64, label: str };
pub fn add(s: State, n: i64) i64 { ... }
fn internal() i64 { ... } // not reachable from outsideOnly a qualified lookup checks visibility. An unqualified name can only mean
this module’s own or the prelude’s, and both are always visible, so pub costs
nothing to resolve.
Re-export
A const may be a second name for something another module declares, which is
what lets a package of several files present one of them:
const inner = @import("./inside.ws");
pub const Pair = inner.Pair; // a type
pub const twice = inner.twice; // a function, or a whole overload setThe alias and the original are the same type and the same function set rather than copies, so a value made through one is usable through the other, and an overload set renamed once still dispatches on every member.
What a program pays for
A library module is read only if something imports it. A program that mentions
nothing pays for nothing: wsharp check on a ten-line file takes about five
milliseconds however far the library grows.
The HTTP status types go further. They are materialised on first mention, so a program that never names one carries none of them.
Modules are also workers
There is no separate declaration form for a service. A module with an init that
makes its state, and functions taking that state as their first parameter, is
something @spawn can start:
// A service: a module with an `init` that makes its state, and functions
// taking that state as their first parameter. No new declaration form -- W#
// has no mutable globals, so a worker's state had to be an explicit value
// passed in and out, and once it is, the functions that take it are exactly
// the things the worker can be asked to do.
pub const State = struct { total: i64, label: str };
pub fn init(start: i64, label: str) State {
return State{ .total = start, .label = label };
}
pub fn add(s: State, n: i64) i64 {
s.total = s.total + n;
return s.total;
}
pub fn total(s: State) i64 { return s.total; }
pub fn describe(s: State) str { return s.label; }
// Not a method: it does not take the state, so no caller can reach it through
// a handle.
pub fn helper() i64 { return 0; }helper does not take the state, so no caller can reach it through a handle. That
is the next page.