Skip to content
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

feat: checked cast with TypedMatcher #8394

Merged
merged 7 commits into from
Jul 12, 2024
Merged

feat: checked cast with TypedMatcher #8394

merged 7 commits into from
Jul 12, 2024

Conversation

turadg
Copy link
Member

@turadg turadg commented Sep 27, 2023

refs: #6160

Description

I ran again into the need for type narrowing with a shape object: https://github.com/Agoric/agoric-sdk/pull/8385/files#diff-b17d46d065ac769cdf1d70471b16d141f28672965c18522d58d39722d4852ad4R329-R331

endojs/endo#1721 approached the general problem of inferring the type from the shape. But until we have that, we can at least move the type description onto the shape object so users of that shape can automatically get the type a match implies.

With this, the code referenced above would be,

const questionDesc = cast(info, QuestionSpecShape);

Security Considerations

n/a

Scaling Considerations

n/a

Documentation Considerations

Once this settles in agoric-sdk, we may want to move it into Endo. Alternately, Endo waits for more general support.

Testing Considerations

Has a test. Maybe should use tsd instead of Ava

Upgrade Considerations

n/a

@turadg
Copy link
Member Author

turadg commented Sep 28, 2023

While searching for other places to use this, most of the shape checks are to validate the type that a value already has statically. So there's no value in the narrowing. For example, a function param foo: Foo.

We could have some utility type for indicating the soundness of the type annotation.

One way is a utility type Expect<> for "this ought to be type Foo but we don't know yet". Then functions that actually validate the type could have foo: Expect<Foo> as a parameter. That would maintain the documentation aspect of the parameters but also let the type be narrowed. Expect<Foo> would have to accept Foo as an argument so the tag would be optional. That means, since TS is structural, that anything could use an Expect<Foo> as if it were a Foo, but at least there would be an indication that it hasn't been validated. We could have a linter verify rules around use of Expect types.

Going the other way, we could have a Sound<> type that says "this claimed to be Foo and some code has run to verify that`. This perhaps aligns more with the reality of TS in that naked types have no expectation of soundness. But it seems less pragmatic to me and more verbose.

Copy link
Member

@michaelfig michaelfig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one comment, otherwise LGTM!

@@ -18,3 +20,17 @@ export declare class SyncCallback<

public isSync: true;
}

declare const type: unique symbol;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little weird to me to use the identifier type here when it's also a TypeScript keyword. Could you please name it something like TypedMatcherParameter or anything that you think would be better.?

@erights
Copy link
Member

erights commented Sep 30, 2023

@turadg , while I would still appreciate the verbal explanation we arranged, please don't wait on that to merge this. I understand this well enough that I'd be happy (overjoyed, actually) to see it merged based on @michaelfig 's approval. You can then clear up my remaining confusions post-merge.

@turadg
Copy link
Member Author

turadg commented Sep 30, 2023

@erights before merging I'd like to include some usages of it in this PR, to validate the design. If you have some in mind feel free to push commits.

Copy link
Member

@erights erights left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My memory of this has gotten stale, so perhaps I forgot. But,

Why is this in agoric-sdk? It seems much more natural to include it in endo, either @endo/patterns or @endo/exo. Isn't this useful for anything building on those?

@turadg
Copy link
Member Author

turadg commented Dec 1, 2023

It seems much more natural to include it in endo

That it would be useful in Endo does not seem to me a reason to exclude it from agoric-sdk.

I started in in agoric-sdk so that I could evaluate its utility in agoric-sdk and iterate on the design. Once the design and implementation are acceptable,I think the next step is to land it here. Then a separate PR can copy it to Endo and a third PR can make agoric-sdk use that once it's published.

@erights
Copy link
Member

erights commented Dec 1, 2023

That seems like 3x as much work, spread out over at least 10x as much time. If we're agreed that the correct final destination is, say, @endo/patterns, why not just do it?

I suspect we have conflicting unstated assumptions, so probably best to postpone this to a more general engineering discussion. cc @ivanlei @kriskowal

@turadg
Copy link
Member Author

turadg commented Mar 28, 2024

If we're agreed that the correct final destination is, say, @endo/patterns, why not just do it?

  1. The effort required delays my work developing our SDK product
  2. The calendar wait to get it into Endo and then wait for an Endo sync delays its availability to anyone in SDK
  3. It may merit some adjusting that can only be determined from active use. Endo is meant to change more slowly and take things that are battle tested.
  4. If we hadn't had this hurdle the PR would have been available in agoric-sdk 6 months ago

@michaelfig
Copy link
Member

I suspect we have conflicting unstated assumptions,

My assumptions: adding code to Agoric SDK should be frictionless and entirely based on what pragmatically benefits our platform. We should not implement experimental features in Endo until we have gained experience with them and can justify the additional cost of maintaining them for a wider audience.

I am in favour of exploring the path laid out in this PR, and committing it to Endo once we have learned its shape better.

Copy link

cloudflare-workers-and-pages bot commented Apr 24, 2024

Deploying agoric-sdk with  Cloudflare Pages  Cloudflare Pages

Latest commit: 20e1779
Status: ✅  Deploy successful!
Preview URL: https://2791640a.agoric-sdk.pages.dev
Branch Preview URL: https://ta-typed-shapes.agoric-sdk.pages.dev

View logs

@turadg turadg requested a review from erights April 24, 2024 00:27
@erights
Copy link
Member

erights commented Apr 24, 2024

I suspect we have conflicting unstated assumptions,

My assumptions: adding code to Agoric SDK should be frictionless and entirely based on what pragmatically benefits our platform. We should not implement experimental features in Endo until we have gained experience with them and can justify the additional cost of maintaining them for a wider audience.

I am in favour of exploring the path laid out in this PR, and committing it to Endo once we have learned its shape better.

This argument presumes there is some clear conceptual boundary between agoric-sdk and endo, such that we're willing to put experiments on agoric-sdk master that we're not willing to put in endo master. If the argument were to keep an experiment encapsulated within the one package that uses it, that I'd understand. A package is a modularity boundary I understand. But once it gets outside a single package, I don't get it.

Most things that start out this way never take the second step, leaving them misplaced for ages. That migration is never the most urgent thing to do right now, so it never gets done.

But since the choice seems to be to approve having it start in the wrong place vs not having it at all, I will review this as an agoric-sdk PR. Please do file an issue to migrate it to endo when "ready". Then we'll at least have a better chance at eventually getting around to it.

@erights
Copy link
Member

erights commented Apr 24, 2024

If we're agreed that the correct final destination is, say, @endo/patterns, why not just do it?

  1. The effort required delays my work developing our SDK product
  2. The calendar wait to get it into Endo and then wait for an Endo sync delays its availability to anyone in SDK
  3. It may merit some adjusting that can only be determined from active use. Endo is meant to change more slowly and take things that are battle tested.
  4. If we hadn't had this hurdle the PR would have been available in agoric-sdk 6 months ago

I do understand and sympathize with these latency costs. I've paid them many times, with changes that should have taken a day instead taking months (attn @ivanlei @kriskowal ). But it is worth noting that if we did an endo-release-agoric-sdk-sync every night, most of this latency would disappear.

So please at least do file that issue. Thanks.

@erights
Copy link
Member

erights commented Apr 24, 2024

@turadg , since your endojs/endo#2238 is in flight right now, could you bundle these changes with that? I'd be much happier reviewing them there ;)

@erights
Copy link
Member

erights commented Apr 24, 2024

@turadg , since your endojs/endo#2238 is in flight right now, could you bundle these changes with that? I'd be much happier reviewing them there ;)

I am proceeding with the review here now. I did not mean to imply otherwise.

Copy link
Member

@erights erights left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for the one declaration that I cannot yet review, all the rest LGTM.

packages/vat-data/src/exo-utils.js Outdated Show resolved Hide resolved
@turadg turadg marked this pull request as draft June 12, 2024 12:43
@turadg
Copy link
Member Author

turadg commented Jul 12, 2024

the recent TypedPattern change prompted me to clean this up to land

@turadg turadg marked this pull request as ready for review July 12, 2024 00:44
specimen: unknown,
pattern: P,
label?: string | number,
) => asserts specimen is P extends TypedPattern<any> ? PatternType<P> : unknown;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I understand it, OMG this is cool!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this eventually migrate into endo? Into the @endo/patterns package? I hope so. If you agree, would be good to comment on that expected migration here, or somewhere relevant.

Similarly, should #6160 have been an endo issue rather than an agoric-sdk issue, with the eventual fix also eventually being in endo?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you agree, would be good to comment on that expected migration here, or somewhere relevant.

I expect it'll happen when there's some trigger.

should #6160 have been an endo issue rather than an agoric-sdk issue, with the eventual fix also eventually being in endo?

Could have been. Though it's much faster to being useful by making it work in agoric-sdk and moving it to Endo when it's stable.

*/

/**
* @template {AssetKind} K
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, which I've written code like this template declaration, I revise it to

Suggested change
* @template {AssetKind} K
* @template {AssetKind} [K=AssetKind]

since the supertype constraint is usually also a fine default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case there's no way the to omit the parameter because it's positional

@@ -0,0 +1,23 @@
// @ts-check
import { mustMatch as typelessMustMatch } from '@endo/patterns';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we eventually move this into @endo/patterns so the only mustMatch is the typed one, and the typeless one is never exported? (Modulo the transition costs, which I expect will be painful but tolerable.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think once we've had some stress testing of it so we know it won't change much.

packages/orchestration/src/typeGuards.js Outdated Show resolved Hide resolved
/**
* @import {MustMatch} from '@agoric/internal';
*/
import { mustMatch as typelessMustMatch } from '@endo/patterns';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a layering problem causing this duplication, or could all uses of this one use the @agoric/internal one instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agoric/store is actually lower than @agoric/internal, unfortunately

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Could it be moved and exported from @agoric/store for now? As a permanent home, that would be weird. But if it's ultimate destination is @endo/patterns, well, @agoric/store still does deprecated reexporting of many things from @endo/patterns, since patterns lived there first.

I leave this to your esthetics. Your status quo is not doing an offensive amount of duplication; just a tiny amount.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that before merging, so I won't merge tonight

@@ -1,9 +1,16 @@
import { Nat } from '@endo/nat';
import { Fail, q } from '@endo/errors';
import { mustMatch } from '@endo/patterns';
import { mustMatch as typelessMustMatch } from '@endo/patterns';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I'm confused. This is unlikely to be a layering constraint. Why not use the one from @agoric/internal ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because @agoric/time currently doesn't depend on anything in @agoric

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason not to change it to depend on @agoric/internal? When it doesn't create a layering problem, that's what I'd do.

Copy link
Member Author

@turadg turadg Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I defer to @warner. If no objections then I'll do it in a follow-up PR. (probably importing from @agoric/store which is almost Endo :)

packages-graph-tred

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it is thematically what @agoric/internal is for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad you like the tred visualization ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, using @agoric/store as a staging ground for moving to endo makes sense to me. Thanks.

Copy link
Member

@erights erights left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I'm excited to start using this! Thanks.

@turadg turadg added the automerge:rebase Automatically rebase updates, then merge label Jul 12, 2024
@mergify mergify bot merged commit 17608dc into master Jul 12, 2024
78 checks passed
@mergify mergify bot deleted the ta/typed-shapes branch July 12, 2024 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:rebase Automatically rebase updates, then merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants