-
Notifications
You must be signed in to change notification settings - Fork 55
Scenarios #76
Scenarios #76
Conversation
These are notes that are higher-level than a formal spec, but more detailed than makes sense for the Explainer.
Pulled change out of Explainer for now This reverts commit f0ed505.
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.
Overall this looks great. I think the point of the working-notes and scenarios dirs is that they don't have to contain only things we've agreed on every last detail of, so I think it's good to merge with the following requested changes and we should strive to keep these docs updated as we converge on all the details. Thanks!
| credit card: | ||
|
|
||
| ```wat | ||
| (@interface datatype @cc |
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.
For symmetry with how we have (func $f)/(@interface func $f), how about (type $T)/(@interface type $T) (instead of datatype @T)?
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.
Am open to adjustment. However, when defining types there are two basic scenarios: defining a new algebraic type and type aliasing. These need to be distinguished. (Examples of 'type aliasing' include type imports)
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.
@fgmccabe, the only reason to distinguish them would be if you wanted nominal typing for algebraic types. But there is little reason to want that in Wasm interfaces. In fact, there is good reason not to want that because it makes everything more complicated and aversely affects modularity.
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.
On second thoughts, I think we need to keep the @ character.
If you have a param specification for an interface function; you need to disambiguate:
(param $cc (resource $connection))
could mean a param block with two types that happen to be by reference or a single parameter called cc.
On the other hand
(param @cc (resource @connection))
is unambiguous (a param block of two types)
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.
Ah, interesting question. If we look at the GC proposal, which similarly introduces type definitions and uses, we can see that, in most cases, a type use occurs as part of a reference type, so you can unambiguously have (ref $T) for some typedef $T. However, there is one exception: when the field of a struct or element of an array is stored inline, not by reference; for this the explainer uses (type $T) which is presumably chosen to be symmetric with the MVP text format, where you can write:
(module
(type $F (func (param i32) (result i32)))
(func $f (type $F) ...)
)as the expanded form of the syntactic sugar:
(module
(func $f (param i32) (result i32) ...)
)Thus, I think your final example would be most-symmetrically written:
(param (type $cc) (resource $cc))|
|
||
| ```wat | ||
| (@interface datatype @cc | ||
| (record "cc" |
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.
We should discuss whether it's useful to add string names to type definitions. I think it's necessary to name fields (since it allows specifying field type compatibility in an order-independent manner, which is useful), but I don't think it buys us anything to name structs, given that we'll already need to check their contents for type-compatibility anyway.
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.
If you have alternate cases in an algebraic type then you need to distinguish them. This is going to be an issue especially for C ... to be handled later.
It boils down to a couple of issues: there are certain specific cases which are very common and which are technically instances of algebraic type definitions - do we wish to honor that heritage; and there are also legitimate API scenarios where there is variation in the data being passed.
The first is essentially the option type and also the either type (used to distinguish normal from error cases).
The second includes supporting the kind of polymorphism that is quite common in the wild, and in JS APIs.
Having different cases allows us to capture the variants that show up in APIs without inventing new names where there weren't before: e.g.,
addEventListener('type', function, capture?);
addEventListener('type', function, options?);
addEventListener('type', Object handler, capture?);
addEventListener('type', Object handler, options?);
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.
Interesting point. With the current interface types proposal, one never does an "overload resolution" wherein a (name, signature) pair is looked up in some scope, resolving to a function. Rather, the order is inverted: a function is supplied to wasm instantiation and then we ask if that function's type is compatible with the import's declared type. For Web IDL, all declared overloads end up producing a single function that wasm can import and thus the type-compatibility check is against this list of valid signatures. For future APIs (like WASI), I think we'd want to not allow overloading and instead use variant types in the signature (which, of course, have string field names).
| (@interface func (export "countCodes") | ||
| (param $str string) (result u32) | ||
| local.get $str | ||
| string-to-memory $memx "malloc" |
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.
nit: I think $memx should be "memx", since it's a core export.
| (param $str string) (result u32) | ||
| local.get $str | ||
| string-to-memory $memx "malloc" | ||
| call "countCodes_" |
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.
nit: call-export
| local.get $ptr | ||
| local.get $len | ||
| string.copy Mi:"memi" Mx:"memx" Mx:"malloc" | ||
| call Mx:"countCodes_" |
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.
Same comment as above about using memory.copy and local module names instead of Mx:"export".
|
Respectfully demur.
The option type is a special case of algebraic type; as is the either type.
So, do we wish to honor that or do we make a bunch of special cases? The
problem with special cases is that there are so many of them ... including
those not yet invented.
In addition, there are enough examples of APIs that are quite polymorphic
in nature, and having separate imports for them is a hack necessary for C
programming but should not necessarily be reflected in the 'interface'.
Some examples: settimeout (string or callback function argument),
define_property, canvas get_context, bufferData, etc.
In addition, I do not understand your comment about modularity.
…On Tue, Oct 15, 2019 at 9:27 AM Andreas Rossberg ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In proposals/interface-types/working-notes/record.md
<#76 (comment)>
:
> @@ -0,0 +1,73 @@
+
+
+### Records
+
+Records are collections of named and typed fields. Records can be defined in as
+Interface Types; for example this definition encodes a familiar notion of a
+credit card:
+
+```wat
***@***.*** datatype @cc
@fgmccabe <https://github.com/fgmccabe>, the only reason to distinguish
them would be if you wanted nominal typing for algebraic types. But there
is little reason to want that in Wasm interfaces. In fact, there is good
reason not to want that because it makes everything more complicated and
aversely affects modularity.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#76?email_source=notifications&email_token=AAQAXUBGIHHWGFRLDYELH5LQOXVPTA5CNFSM4I7POCDKYY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCIAMNUQ#discussion_r335055258>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAQAXUFOFYJ2NSSQ5INIY2LQOXVPTANCNFSM4I7POCDA>
.
--
Francis McCabe
SWE
|
Not sure if this is a reply to my comment? I definitely agree that you want variant types. I was merely saying that there is no reason to make them nominal, and thereofore, no reason to distinguish type from datatype definitions. You can interpret definitions of algebraic data types as aliases for structural variant/sum types just fine, and it's actually much easier to do so in the context of interfaces.
Fair enough. I could go into a long explanation about how nominal types don't work at interface boundaries, esp with the strong kind of modularity provided by Wasm modules (which has no notion of referring to types from other modules). But that may be a bit off-topic. |
|
The case for nominal types in the interface layer has nothing to do with
wasm core types.
Put simply, for APIs it is important that a person is never misunderstood
as a chair or a port.
On Tue, Oct 15, 2019 at 11:03 PM Andreas Rossberg ***@***.***> wrote:
@fgmccabe <https://github.com/fgmccabe>:
The option type is a special case of algebraic type; as is the either
type. So, do we wish to honor that or do we make a bunch of special cases?
The problem with special cases is that there are so many of them ...
including those not yet invented.
In addition, there are enough examples of APIs that are quite polymorphic
in nature, and having separate imports for them is a hack necessary for C
programming but should not necessarily be reflected in the 'interface'.
Some examples: settimeout (string or callback function argument),
define_property, canvas get_context, bufferData, etc.
Not sure if this is a reply to my comment? I definitely agree that you
want variant types. I was merely saying that there is no reason to make
them nominal, and thereofore, no reason to distinguish type from datatype
definitions. You can interpret definitions of algebraic data types as
aliases for structural variant/sum types just fine, and it's actually much
easier to do so in the context of interfaces.
In addition, I do not understand your comment about modularity.
Fair enough. I could go into a long explanation about how nominal types
don't work at interface boundaries, esp with the strong kind of modularity
provided by Wasm modules (which has no notion of referring to types from
other modules). But that may be a bit off-topic.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#76?email_source=notifications&email_token=AAQAXUD7DYMF3H25GQXAMA3QO2VEJA5CNFSM4I7POCDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBLGRLQ#issuecomment-542533806>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAQAXUBJATPKL34QBD57P5TQO2VEJANCNFSM4I7POCDA>
.
--
Francis McCabe
SWE
|
Neither has my argument. ;) They simply won't work in interfaces for a Wasm-like module system. How would you use a nominal datatype from another module?
Confusing two structurally equivalent algebraic types is no different from confusing an int denoting a length with an int denoting an index. And that latter confusion is waaaay more likely to happen in practice. Yet, APIs don't tend to define nominal integer types. |
|
The case for algebraic data types is a bit different: if you have variant
types you need to be able to signal which variant you actually have. This
is data dependent; it seems to me.
On Wed, Oct 16, 2019 at 12:05 AM Andreas Rossberg ***@***.***> wrote:
@fgmccabe <https://github.com/fgmccabe>:
The case for nominal types in the interface layer has nothing to do with
wasm core types.
Neither has my argument. ;) They simply won't work in interfaces for a
Wasm-like module system. How would you use a nominal datatype from another
module?
Put simply, for APIs it is important that a person is never misunderstood
as a chair or a port.
Confusing two structurally equivalent algebraic types is no different from
confusing an int denoting a length with an int denoting an index. And that
latter confusion is waaaay more likely to happen in practice. Yet, APIs
don't tend to define nominal integer types.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#76?email_source=notifications&email_token=AAQAXUBC72RLFAGQ5XDQ6ZDQO24MVA5CNFSM4I7POCDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBLLXZI#issuecomment-542555109>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAQAXUC5J2ZV3GAGLLUNEDDQO24MVANCNFSM4I7POCDA>
.
--
Francis McCabe
SWE
|
Sure, but that is independent from making the type nominal (i.e., not an alias). Variant tags can be treated just as structurally as record labels (in fact, they are the exact dual). They can both be identified purely by syntactic name -- or in Wasm, rather by index. Knowing the structure of the assumed type is enough to interpret the label/index. Really no different from integers ()which are really just a shorthand for peano numbers, so datatypes :)). |
I have refactored the scenarios into a series of smaller files to make them more digestible.
Also, the directory.md example is kind of interesting...