-
Notifications
You must be signed in to change notification settings - Fork 17
Consider adding type contracting (protocols/interfaces/abstract types) #9
Description
In dynamic, interpreted languages, the ability to define explicit contracts about the interfaces that values expose is often lost. Instead, these are traded for abstract definitions and/or implicit contracts that are often visually simpler, but less obvious when the contract is extended or re-implemented by another class/module.
The primary loss in these languages is the ability to enforce those contract requirements before they are encountered at runtime (if they ever are).
Another great side-effect of being able to enforce those contracts is predictable failure. That is, if a function requires that a contract be satisfied, then calls to that function with non-conforming arguments will fail before any of the function's body is executed. When the contract is not enforceable, it is common for functions to begin execution and fail midway through, resulting in a greater (often inferred, not necessary) dependence on exceptions and exception handling.
I think having Myst adopt a contracting system would be beneficial and worthwhile, and doesn't have to compromise readability or flexibility in the language. The roadmap for Myst already suggests explicit typing will be idiomatic in most cases, so the ability to specify more abstract behaviors follows suit to me.
Examples of languages that implement and enforce contracting systems:
- Elixir has Protocols and Behaviours which are distinct, but both provide guarantees of working interfaces at a contract-like level.
- Java has Interfaces which are almost identical to the contracts explained here, but lack some functionality (and are extremely verbose).
- C++ has virtual functions that can be added to superclasses as a simplistic version of a contract.
- Crystal has abstract types that are similar to virtual functions in C++.
Some considerations:
- What does the syntax look like? I'm somewhat partial to something like a
useorimplementsdirective at a module/class level for defining the contracts that the type should satisfy. - How is this affected by re-openable classes? When is the contract enforced? After parsing finishes, or some time during interpretation (function call time?).
- Is anything more than module inheritance necessary? In most cases, contracts can be implicitly set up by defining and including modules with the contract definition into subtypes (see Ruby). This is effective, but still has most of the drawbacks of implicitness as mentioned earlier.