-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: far classes with interface guards, used by ERTP #5960
Conversation
072cfff
to
49d520a
Compare
b65762a
to
bf37be3
Compare
49d520a
to
3b775e1
Compare
d66b625
to
bc94cb3
Compare
bc94cb3
to
e75743d
Compare
Reviewers, Not really ready for review. Please ignore for now and pay attention onto to #5970
If someone understands what I need to do to tickle CI into running all our normal tests, please let me know. |
48d13da
to
874c1dd
Compare
Draft PR, or for that matter PRs that are not ready for merge don't run integration tests. Those can be forced with the |
Deployment test failed because of #5970 (comment). Not sure why getting-started failed |
d0534b0
to
48f3063
Compare
874c1dd
to
6593737
Compare
Still more typing work to be done, but recording this for now. This PR introduces a "thisful" style as an alternative, where the context is passed as Advantage thisful style: Advantage context-argument style, arguably: |
One thing I noticed is that One mitigation might be to cheat and have the If we considered only developer experience, really the state and facets would be something like private fields ( class Counter {
@state #state;
@facets #facets;
inc(n = 1) {
return this.#state.value += n;
}
} A cheap alternative might be to use symbols, but that may require making the behavior declaration a "maker": defineKind('counter', () => ({ value: 0 }), ({ stateSym, facetsSym }) => ({
inc(n = 1) {
return this[stateSym].value += n;
},
})); |
48f3063
to
0493dc1
Compare
6593737
to
2185df0
Compare
@Chris-Hibbert @gibson042 |
46b6406
to
171448a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fairly comfortable with the consequences in the ERTP code. The implementation is beyond me to verify.
I doubt I can make use of the tools in any novel cases without help. There are quite a few examples in the ERTP code to copy from, but I'll bet governance (for example) does things that ERTP doesn't, and I don't see documentation on the semantics or usage of the M.whatever()
functions.
I can intuit the meaning of
M.callWhen(M.await(PaymentShape))
.optional(M.pattern())
.returns(amountShape),
from context, but I'm not confident that that will be enough for me to use the tools without a fair number of questions.
Reviewers, do you agree that the following can wait until after this PR? TODO: Extract some of the text above to serve as documentation of Far Classes without reference to kinds. This can serve as an explanation to programmers who started with Far classes and never need to learn about kinds. TODO: Provide FarClass definers of normal in-memory heap objects #6107 TODO: Deprecate kinds #6106 TODO: Describe the pattern sublanguage, including the new interface guards. TODO: Explore flatter TODO: conventional class programmers are used to doing composition by inheritance. For the objects-as-closure style currently supported by |
Done. |
I agree. Some of the doc is a hurdle for me, but that's about follow-on work, not this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing to comment
to prevent accidental submission based only on this review.
The parts I understand still LGTM.
171448a
to
12dfbe1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@erights LGTM!
Also, I agree with the proposed TODOs. |
12dfbe1
to
ef286d1
Compare
Governance succeeds by using the "shared method pattern" to include governance operations when the class is defined rather than trying to stick them onto the instance at the end. As far as I'm concerned that's better anyway, and is a better match for future inheritance. |
Prior to this PR, virtual and durable far objects could be written using the various "kind" definers
define*Kind
vsdefine*DurableKind
)define*Kind
vsdefine*KindMulti
)defineDurableKind*
vsvivifyKind*
)vivifySingleton
)In practice we expect only three to be common (See #5708 )
vivifyDurableKind
vivifyDurableKindMulti
vivifySingleton
This PR introduces replacements for these, in order to address two problems:
context
first argument for receiving the objectstate
and access toself
or cohort offacets
. This shifts the argument types by one, which creates a painful IDE experience (See fix: far classes with interface guards, used by ERTP #5960 (comment) below. (Note that singletons do not have this problem.)The replacements
defineVirtualFarClass
vsdefineDurableFarClass
)define*FarClass
vsdefine*FarClassKit
)defineDurableFarClass*
vsvivifyFarClass*
)vivifyFarInstance
)Its worth noting that we expect conversion from Kind style to Far Class style not to effect which are common in practice:
vivifyFarClass
vivifyFarClassKit
vivifyFarInstance
It addresses the two problems mentioned above
context
as theirthis
argument, leaving their function type unaffected. This requires that these methods be written as functions that receive theirthis
binding dynamically. We chose to use concise method syntax, where all these methods are written inline within the call to the FarClass definer. Note that these constraints are familiar to JSclass
programmers, whose methods must also be written in concise method syntax that must appear inline within theclass
definition.M.interface
and helpersM.call
,M.callWhen
, andM.await
for defining protective interfaceGuards that can express and enforce type-like constraints on incoming arguments and return results. This sublanguage needs to be separately documented. The FarClass definer functions all take aninterfaceGuard
argument. The methods exposed as the API will check all their incoming arguments before calling the raw methods provided in the same call to the FarClass definer.The raw methods can thus assume that all the validation expressed by the interfaceGuard for this method has already been checked. It is still the responsibility of the raw method to validate all issues that remain after the interfaceGuard has done its part of the job. For example, for a payment argument, the interfaceGuard can only validate that it is a remotable. It remains to the raw method to manually check, for example, that it is a live payment of the correct brand containing enough assets.
This raises the other reason we need to write these methods inline: By themselves, they are fragile. Also, by themselves, they are
this
-sensitive functions. For both of those reasons, we'd like to avoid the possibility of them floating around as first-class values. By having them appear inline in the FarClass definer call, we successfully encapsulate them. Afterward, they can only be reached by the protective outer shell described by the interfaceGuard.After this PR is merged and we have experience using it, we hope to decide to deprecate the Kind functions and style in favor of the Far Class style. But since this decision should follow experience, we leave it to the follow on PR #6106 .