You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Proposal #2006 adds value expressions to Carbon, one key feature is the ability to have customized representations for values of a given type.
This leads to a few related questions:
How should a type request such a customization?
Should the customization be exposed to generic code?
If so, how should that exposed type be modeled? The obvious way is as an associated type of an interface -- what should the interface and type be named, and how should they relate to (1)?
Details:
Background
Proposal #2006 codifies Carbon's expression categories, including value expressions. Beyond just classifying expressions, these in turn are modeled directly in Carbon's function parameters as value parameters. These parameters work much like const & parameters in C++ do, but have the advantage that they are allowed to be represented in a more efficient way when desired -- such as a copy in a machine register.
There are two value representations that are expected to be very common for a type T:
const T: the value representation is a (constant) copy, maybe in a machine register like an i32 might be.
const T*: the value representation is a (constant) pointer, avoiding a potentially expensive copy, much like a C++ const & might.
One option is for types to implement an interface:
class IntLike {
// ...
impl asValueRep where .Rep=const IntLike;
}
class BigAndSlowToCopy {
// ...
impl asValueRep where .Rep=const BigAndSlowToCopy*;
}
class String {
// ...
impl asValueRep where .Rep= StringView;
}
However, this has some challenges. First, we would like there to be a default for the majority of types, but that default to be different for different types -- the compiler should pick a "good" default. We could try to encode this as a template impl of the interface, but it isn't clear how or when a type stops being able to provide its own. Immediately after the type is fully defined we might want to understand how to work with a value expression.
A second issue that came up in informal discussions was that this is a very significant behavior to signal through an implementation of an interface, and we might want a more distinct syntax to highlight this.
We could imagine some custom syntax, and in fact #2006 currently uses a placeholder syntax:
class IntLike {
// ...value_rep=const Self;
}
class BigAndSlowToCopy {
// ...value_rep=const Self*;
}
class String {
// ...value_rep= StringView;
}
This placeholder syntax intentionally isn't great. This issue is the place to suggest other, better syntaxes that we might use.
We had a situation with some similarity but also some important differences with destructors and chose dedicated syntax.
Question (2): should this customization be exposed generically?
Once we have some answer to (1), we can imagine wanting to access that in a generic context. Should that be supported?
If so, the most obvious approach would be an interface that is always implemented for types. If (1) uses an interface, we're kinda done. But if (1) uses explicit syntax, how should that manifest in an interface implementation with an associated type?
One concern with doing this is that this customization isn't very generic in any sense. What you can do with a T varies in non-generic ways between a value representation of const T* and some unrelated U. The semantics could be copy-based with a const T rep, or by-reference with a const T*, or even a mixture.
On question (2): one possibility is: there might be one interface that is used for types with a custom value representation, that has a Convert method and extends another interface (ValueRepresentation) that has an associated type with the value representation type. The Convert method doesn't make sense for all types, but the associated type does.
I think the syntax problems here are going to be very similar to what we are going to face with defining "move" for a class, and we should have a consistent solution to both problems (along with maybe destructors). I recommend first figuring out what parameters we need to specify the move behavior of a class, and then consider syntactic options for customizing these class behaviors together.
Summary of issue:
Proposal #2006 adds value expressions to Carbon, one key feature is the ability to have customized representations for values of a given type.
This leads to a few related questions:
Details:
Background
Proposal #2006 codifies Carbon's expression categories, including value expressions. Beyond just classifying expressions, these in turn are modeled directly in Carbon's function parameters as value parameters. These parameters work much like
const &
parameters in C++ do, but have the advantage that they are allowed to be represented in a more efficient way when desired -- such as a copy in a machine register.There are two value representations that are expected to be very common for a type
T
:const T
: the value representation is a (constant) copy, maybe in a machine register like ani32
might be.const T*
: the value representation is a (constant) pointer, avoiding a potentially expensive copy, much like a C++const &
might.There is a more rare third option: a type may provide an entirely custom value representation. This can be especially useful when there are well established or particularly efficient "view" types, for example C++'s
std::string_view
. A detailed example from the proposal can be found rendered nicely here: https://github.com/chandlerc/carbon-lang/blob/pointers2/docs/design/values.md#value-representation-and-customizationQuestion (1): How should types customize this?
One option is for types to implement an interface:
However, this has some challenges. First, we would like there to be a default for the majority of types, but that default to be different for different types -- the compiler should pick a "good" default. We could try to encode this as a template
impl
of the interface, but it isn't clear how or when a type stops being able to provide its own. Immediately after the type is fully defined we might want to understand how to work with a value expression.A second issue that came up in informal discussions was that this is a very significant behavior to signal through an implementation of an interface, and we might want a more distinct syntax to highlight this.
We could imagine some custom syntax, and in fact #2006 currently uses a placeholder syntax:
This placeholder syntax intentionally isn't great. This issue is the place to suggest other, better syntaxes that we might use.
We had a situation with some similarity but also some important differences with destructors and chose dedicated syntax.
Question (2): should this customization be exposed generically?
Once we have some answer to (1), we can imagine wanting to access that in a generic context. Should that be supported?
If so, the most obvious approach would be an interface that is always implemented for types. If (1) uses an interface, we're kinda done. But if (1) uses explicit syntax, how should that manifest in an interface implementation with an associated type?
One concern with doing this is that this customization isn't very generic in any sense. What you can do with a
T
varies in non-generic ways between a value representation ofconst T*
and some unrelatedU
. The semantics could be copy-based with aconst T
rep, or by-reference with aconst T*
, or even a mixture.Any other information that you want to share?
This leads question is extracted from a discussion in #2006 here: https://github.com/carbon-language/carbon-lang/pull/2006/files#r1244089593
The text was updated successfully, but these errors were encountered: