Open faultyserver opened 7 years ago
I spent a solid hour writing out an example usage of a ValueStore
protocol as an abstract interface for storage types like YAML, JSON, databases, etc. What I got out of it is that there isn't much benefit to them beyond what including a module would be able to provide.
I like the distinction between "supplies this interface, but may fallback to a default implementation" that modules provide and "supplies this interface completely on it's own" from protocols. The latter seems useful, particularly for things like storage types, but I don't see a real use for them.
I'll leave this open in case a more convincing use case comes up.
I think I've found one legitimate use case for some kind of protocol enforcement, and it's actually very simple.
Take the following class:
class SomeCollection
include Enumerable
end
This is semantically valid and not incorrect in any way. SomeCollection
will now have access to all of the methods from Enumerable
.
But, this obviously isn't actually correct, because there's no each
method defined, meaning pretty much every method from Enumerable
will end up raising an error.
This is something that could potentially be resolved by protocols before the code runs. This wouldn't be possible in one pass, as each
could be defined lexicographically after the include
. However, with a semantic/typing pass preceding interpretation (which I already think will be done to enforce function parameter structures), the set of methods defined on the class could be known before the include
is evaluated, and a check for each
could be performed with a protocol.
This would probably make use of an included
hook:
module Enumerable
protocol Each
def each()
end
def included(base)
unless base.implements?(Each)
raise "#{base} included Enumerable, but did not define `#each`."
end
end
end
.implements?
in the above is obviously a placeholder for some native-level check for meeting the protocol (otherwise it would be too slow to warrant something different than a simple responds_to?
implementation). Note that whatever this check ends up being would be the same check used for protocol restrictions in function pattern matching described earlier.
With this, though, Myst could potentially tell you that your code won't work before any code is actually run! (Well, that would need a third pass for handling type finalization separate from evaluation, but it'd be possible! Natively!)
This was an interesting read from José Valim on how to properly implement protocols: http://blog.plataformatec.com.br/2014/06/comparing-protocols-and-extensions-in-swift-and-elixir/.
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:
Some considerations:
use
orimplements
directive at a module/class level for defining the contracts that the type should satisfy.