Declarations
Activating a namespace's bind with use
Importing a type with use also installs the binding its namespace exports for that type.
since 0.1.0-alpha.1linuxwindowswasm
Description
A factory binding written at the top level of a namespace body is that namespace's exported
binding for the type. When code imports the type with use NS::T; (or use NS::T as A;) and T
is a class or interface, the namespace's binding for T is installed in the scope that contains the
use, exactly as if bind T => <that factory>; (or bind A => ...; under an alias) were written
there. Importing the type is therefore enough to receive its default implementation:
use activates the exported bind
namespace Svc {
interface IClock { int now(); }
class RealClock : IClock { int now() => 1000; }
bind IClock => RealClock();
}
use Svc::IClock;
class FakeClock : IClock {
int t;
new FakeClock(int t0) { t = t0; }
int now() => t;
}
void report(IClock clock) { console.writeln(clock.now()); }
void run() {
bind IClock => FakeClock(7);
report();
}
report();
run();
report();
1000
7
1000
report() is filled from the activated binding. Inside run, a bind of the same type is nearer
and shadows it.
Rules
- Only a selective
useof a type activates.uses NS;(including the implicituses std;) never activates a binding.use NS::fn;of a name that is not a type imports the name and activates nothing. Auseof a type whose namespace has no top-level binding for it imports the name and activates nothing, silently. - A written binding beats an activated one in the same scope, without error. The duplicate-binding error applies only to two bindings written in the source.
- Two activated bindings cannot collide: bindings are keyed by type, and importing the same type twice gives the same single binding.
- Nearest wins is unchanged: an activated binding shadows and is shadowed exactly like a written one at the same position.
- An alias changes only the name:
use NS::T as A;activates the same binding.
The standard library uses this: use std::IConsole; activates the system console binding, and
IEnv, IClock, IFileSystem and INet work the same way.
Examples
uses imports names but never activates a binding:
uses does not activate
namespace Svc {
interface IClock { int now(); }
class RealClock : IClock { int now() => 1000; }
bind IClock => RealClock();
}
uses Svc;
void report(IClock clock) { console.writeln(clock.now()); }
report(); // error: missing required argument 'clock'
An alias, a function import that activates nothing, and a written binding that beats the activated one:
Alias, non-type use, and written bind wins
namespace Svc {
interface IClock { int now(); }
class RealClock : IClock { int now() => 1000; }
class Other : IClock { int now() => 5; }
bind IClock => RealClock();
int helper() => 3;
}
use Svc::helper;
use Svc::IClock as Clock;
void report(Clock clock) { console.writeln(clock.now()); }
report();
console.writeln(helper());
void swapped() {
bind Clock => Svc::Other();
report();
}
swapped();
1000
3
5
Substituting a recorder for the system console in a test:
Testing with a fake system capability
use std::IConsole;
class Recorder : IConsole {
Array<string> lines;
void write(string s) { lines = lines.add(s); }
void writeln(string s) { lines = lines.add(s); }
void writeln() { lines = lines.add(""); }
}
void greet(IConsole out) {
out.writeln("hello from greet");
}
void testGreet() {
Recorder rec = Recorder();
bind IConsole => rec;
greet();
console.writeln("recorded: " + rec.lines.length().toString());
console.writeln(rec.lines.at(0));
}
greet();
testGreet();
greet();
hello from greet
recorded: 1
hello from greet
hello from greet
Notes
The capability interfaces are a disciplined alternative to the ambient console, env, File and
socket globals, not a sandbox: the globals remain reachable whether or not a binding is in scope.
Not implemented: binding objects that aggregate several bindings (Bindings values and bind someBindings;). Write ordinary factory bindings, or use use-activated ones, instead.
See also
- Dependency injection: bind and inject — Declare lexically scoped factory bindings that fill unbound parameters automatically, and select a binding explicitly with inject.
- Imports — uses and use — Bring a namespace's names into scope in bulk with
uses, or one name at a time withuse ... as .... - IConsole — What a program may say to the terminal.
- IEnv — What a program may learn from the process environment: its arguments and its variables.
- IClock — What a program may know about the time.
- IFileSystem — What a program may do with the file system: open files and check whether a path exists.
- INet — What a program may do with the network: open connections and listen for them.