Open zygoloid opened 10 months ago
Presumably the syntax will require the forward declaration to use class
or choice
, so the question is whether the keyword used has to match the definition.
If a sum-type class could be forward-declared with choice
, that could make it easier to evolve a choice
type by replacing it with a class
implementation of the same API. However, I'm not sure how important that will be in practice. If I understand correctly, forward declarations will only be possible within a package, so if packages are sufficiently small it may be feasible to change all the forward declarations as part of the implementation change.
If we do allow using choice
to forward-declare a class
definition, I think it should be limited to classes that actually implement the sum type interface, because it would be really confusing if you could use choice
to forward-declare arbitrary classes.
Even with that restriction, it seems a little messy to have cases where there's an arbitrary, formally meaningless choice between choice
and class
. I'm almost tempted to suggest that in general we require types to be forward-declared with class
, regardless of the keyword used to define them. However, that seems liable to be a future FAQ entry ("Why can't I forward-declare my choice type?"), so it's probably better to avoid that.
One interesting consequence of changing the adapter syntax from adapter X for Y { ... }
to class X { adapt Y; ... }
is that we get to forward-declare adapters with class X;
. I wonder if the same logic should be applied to choice types: if we want choice types to notionally be a particular way to define a class, then perhaps they should use syntax beginning with class X
. Then the forward declaration question becomes pretty trivial -- you can forward-declare with class X;
, because that's how you forward-declare any class type.
Syntactically, our approach up to now has been that the portion of the definition from the {
onwards can be replaced by a ;
to form a declaration, which would suggest something like:
class Optional(T:! type) {
choice {
Some(t: T),
None
}
}
... but given that Some
and None
are (among other things) factory functions, perhaps something more similar to a function declaration (eg, with choice
replacing fn
) would be better:
class Optional(T:! type) {
choice None;
choice Some(t: T);
}
This gives us immediate syntactic support for putting access specifiers on the alternatives, adding other member names including member functions to choice types, and so on. (Presumably we wouldn't permit a class to have both var
fields and choice
alternatives, just like we don't permit it to have both var
fields and an adapt
declaration.)
Regarding the latter syntax in particular, I tend to think of the union format that choice
represents as more ordered across name than fn
. For example, in C++ if I write enum class Boolean : uint8_t { False, True };
, that is meaningfully distinct from enum class Boolean : uint8_t { True, False };
because I can also make use of the integer values underlying the enum. Would Carbon support a similar ordering semantic for using the discriminator values?
For comparison, reordering other declarations (e.g., fn
, var
) I don't perceive as changing the value in an equivalent way.
Also, depending on how much you try sharing syntax, while choice
represents tagged unions, you might also want to consider how you might alter syntax for unnamed untagged unions (if they're even supported, e.g. for migrating multiple C++ unnamed union
s on a single class type).
class Optional(T:! type) { choice None; choice Some(t: T); }
If we go that route, it might be nice to take it one step further, and have an evolution path where you can customize the representation while leaving the choice
syntax more or less in place, so that the reader can understand the API of a sum type without having to care whether it has a custom implementation. For example, maybe you could treat the choice
declarations as static function/variable declarations, so that you could customize that definition of Optional
like so:
class Optional(T:! type) {
choice None = { .has_value = false };
choice Some(t: T) { return {.has_value = true, .value = t }; }
private var has_value: bool;
private var value: T;
impl as Match { ... }
}
That's not great, but it's a gesture toward the kind of thing I mean.
If not for that consideration, I would much prefer your other suggested syntax:
class Optional(T:! type) { choice { Some(t: T), None } }
That keeps all the alternatives grouped together, which I think will be helpful for readability if the definition can contain other things like member functions. But maybe we can get the best of both, somehow?
Would Carbon support a similar ordering semantic for using the discriminator values?
We don't have a design for making the discriminator visible in the API (short of doing a full custom implementation) but we'll likely want that at some point, for interop/migration purposes. However, I think it should require the type to explicitly define the discriminator values, rather than have them be inferred based on the order.
Also, depending on how much you try sharing syntax, while
choice
represents tagged unions, you might also want to consider how you might alter syntax for unnamed untagged unions (if they're even supported, e.g. for migrating multiple C++ unnamedunion
s on a single class type).
I think we'll very likely want untagged unions, but trying to share syntax between them seems like it will impose a lot of cost for little benefit. The problems they need to solve don't have much overlap.
We triage inactive PRs and issues in order to make it easier to find active work. If this issue should remain active or becomes active again, please comment or remove the inactive
label. The long term
label can also be added for issues which are expected to take time.
This issue is labeled inactive
because the last activity was over 90 days ago.
Currently, there is no suggestion in the design that choice types support forward declarations, and the toolchain implementation consequently does not support forward declarations of choice types. So:
class
orchoice
?I don't think there are any novel issues with choice type declarations that don't apply to classes. But the second question seems like it might deserve a little thought.