-
Notifications
You must be signed in to change notification settings - Fork 227
Description
Frequently developers want to make sure that particular functionality is accessible only under certain conditions. Use-cases:
- A debug function/method should only be called if assertions are enabled.
- Examples: debugValidateChild, debugFillProperties.
- A platform-specific implementation of a function should only be called if the program is running on that platform.
- Examples: you may want to use CupertinoTheme only when you are running on iOS, you want to use the HttpRequest only on the Web and HttpClient only on Androd and iOS.
- A stateful object may only allow certain method calls when it is in a particular state. More generally, you want to guard member access based on a condition that's outside the member's signature.
- Examples: a button should only be
click()
ed if it is enabled, a tree node may only be detached if it is currently detached,RenderObject.paint()
may only be called during the frame's painting phase, a scrollable view should only scroll after the drag gesture started.
- Examples: a button should only be
- A feature should only be used if it is available.
- Examples: web browsers are in a perpetual catch-up mode with web standards. Frequently, a feature is available in some browsers but not others.
As of today, most of guarding is done via runtime assertions. Unfortunately, this only gives runtime protection and only available during debugging. There is no static checking that could also protect production builds. Let's take the button example. Currently, if you want to protect that button from being clicked when it is disabled you'd typically do the following:
class Button {
bool enabled;
void click() {
assert(enabled);
...
}
}
This has the following issues.
- You can still write, compile and run the following incorrect code:
final b = Button()
..enabled = false;
b.click();
-
The bug manifests itself in production in a hard-to-debug way. Namely, the assertion is not triggered and the program keeps on trucking. The exception may be thrown in a completely unrelated part of the code.
-
The
assert
as a language construct has canned semantics and it not usable for anything other than runtime assertions in the-c
mode. For example, it is not useful for distinguishing between profile and release modes.
Desired properties
(where I say "compiler" I also include the analyzer)
- Statically analyzable: the compiler should be able to catch errors when parts of a program are entered in a wrong state.
- Sound: in the absence of compiler errors it is not possible execute guarded parts of the program. Preferably, most of this is prevented statically, but some runtime checks are acceptable for flexibility.
- Scalable: the solution would not be a large collection of features, but one easy-to-understand language feature that addresses most use-cases.
- Tree-shakeable: parts of programs that cannot possibly be executed for a given build configuration must be tree-shaken off (e.g. debug vs release mode, target platform).