From 82bb5a0e98eb57790b1ad8cd67d4f13640ef7056 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 22 Feb 2016 13:32:07 +0300 Subject: [PATCH 1/2] RFC: Clarify the relationships between various kinds of structs and variants --- text/0000-adt-kinds.md | 169 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 text/0000-adt-kinds.md diff --git a/text/0000-adt-kinds.md b/text/0000-adt-kinds.md new file mode 100644 index 00000000000..e5894ea5509 --- /dev/null +++ b/text/0000-adt-kinds.md @@ -0,0 +1,169 @@ +- Feature Name: clarified_adt_kinds +- Start Date: 2016-02-07 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Provide a simple model describing three kinds of structs and variants and their relationships. +Provide a way to match on structs/variants in patterns regardless of their kind (`S{..}`). +Permit tuple structs and tuple variants with zero fields (`TS()`). + +# Motivation +[motivation]: #motivation + +There's some mental model lying under the current implementation of ADTs, but it is not written +out explicitly and not implemented completely consistently. +Writing this model out helps to identify its missing parts. +Some of this missing parts turn out to be practically useful. +This RFC can also serve as a piece of documentation. + +# Detailed design +[design]: #detailed-design + +The text below mostly talks about structures, but almost everything is equally applicable to +variants. + +## Braced structs + +Braced structs are declared with braces (unsurprisingly). + +``` +struct S { + field1: Type1, + field2: Type2, + field3: Type3, +} +``` + +Braced structs are the basic struct kind, other kinds are built on top of them. +Braced structs have 0 or more user-named fields and are defined only in type namespace. + +Braced structs can be used in struct expressions `S{field1: expr, field2: expr}`, including +functional record update (FRU) `S{field1: expr, ..s}`/`S{..s}` and with struct patterns +`S{field1: pat, field2: pat}`/`S{field1: pat, ..}`/`S{..}`. +In all cases the path `S` of the expression or pattern is looked up in the type namespace. +Fields of a braced struct can be accessed with dot syntax `s.field1`. + +Note: struct *variants* are currently defined in the value namespace in addition to type namespace, + there are no particular reasons for this and this is probably temporary. + +## Unit structs + +Unit structs are defined without any fields or brackets. + +``` +struct US; +``` + +Unit structs can be thought of as a single declaration for two things: a basic struct + +``` +struct US {} +``` + +and a constant with the same name + +``` +const US: US = US{}; +``` + +Unit structs have 0 fields and are defined in both type (the type `US`) and value (the +constant `US`) namespaces. + +As a basic struct, a unit struct can participate in struct expressions `US{}`, including FRU +`US{..s}` and in struct patterns `US{}`/`US{..}`. In both cases the path `US` of the expression +or pattern is looked up in the type namespace. +Fields of a unit struct could also be accessed with dot syntax, but it doesn't have any fields. + +As a constant, a unit struct can participate in unit struct expressions `US` and unit struct +patterns `US`, both of these are looked up in the value namespace in which the constant `US` is +defined. + +Note 1: the constant is not exactly a `const` item, there are subtle differences, but it's a close +approximation. +Note 2: the constant is pretty weirdly namespaced in case of unit *variants*, constants can't be +defined in "enum modules" manually. + +## Tuple structs + +Tuple structs are declared with parentheses. +``` +struct TS(Type0, Type1, Type2); +``` + +Tuple structs can be thought of as a single declaration for two things: a basic struct + +``` +struct TS { + 0: Type0, + 1: Type1, + 2: Type2, +} +``` + +and a constructor function with the same name + +``` +fn TS(arg0: Type0, arg1: Type1, arg2: Type2) -> TS { + TS{0: arg0, 1: arg1, 2: arg2} +} +``` + +Tuple structs have 0 or more automatically-named fields and are defined in both type (the type `TS`) +and the value (the constructor function `TS`) namespaces. + +As a basic struct, a tuple struct can participate in struct expressions `TS{0: expr, 1: expr}`, +including FRU `TS{0: expr, ..ts}`/`TS{..ts}` and in struct patterns +`TS{0: pat, 1: pat}`/`TS{0: pat, ..}`/`TS{..}`. +In both cases the path `TS` of the expression or pattern is looked up in the type namespace. +Fields of a braced tuple can be accessed with dot syntax `ts.0`. + +As a constructor, a tuple struct can participate in tuple struct expressions `TS(expr, expr)` and +tuple struct patterns `TS(pat, pat)`/`TS(..)`, both of these are looked up in the value namespace +in which the constructor `TS` is defined. Tuple struct expressions `TS(expr, expr)` are usual +function calls, but the compiler reserves the right to make observable improvements to them based +on the additional knowledge, that `TS` is a constructor. + +Note: the automatically assigned field names are quite interesting, they are not identifiers +lexically (they are integer literals), so such fields can't be defined manually. + +## Summary of the changes. + +Everything related to braced structs and unit structs is already implemented. + +New: Permit tuple structs and tuple variants with 0 fields. This restriction is artificial and can +be lifted trivially. Macro writers dealing with tuple structs/variants will be happy to get rid of +this one special case. + +New: Permit using tuple structs and tuple variants in braced struct patterns and expressions not +requiring naming their fields - `TS{..ts}`/`TS{}`/`TS{..}`. This doesn't require much effort to +implement as well. +This also means that `S{..}` patterns can be used to match structures and variants of any kind. +The desire to have such "match everything" patterns is sometimes expressed given +that number of fields in structures and variants can change from zero to non-zero and back during +development. + +New: Permit using tuple structs and tuple variants in braced struct patterns and expressions +requiring naming their fields - `TS{0: expr}`/`TS{0: pat}`/etc. This change looks a bit worse from +the cost/benefit point of view. There's not much motivation for it besides consistency and probably +shortening patterns like `ItemFn(name, _, _, _, _, _)` into something like `ItemFn{0: name, ..}`. +Automatic code generators (e.g. syntax extensions like `derive`) can probably benefit from the +ability to generate uniform code for all structure kinds as well. +The author of the RFC is ready to postpone or drop this particular extension at any moment. + +# Drawbacks +[drawbacks]: #drawbacks + +None. + +# Alternatives +[alternatives]: #alternatives + +None. + +# Unresolved questions +[unresolved]: #unresolved-questions + +None. From 433edd99f195d013c2d6e3a350fd2bd939cd88da Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 28 Feb 2016 16:19:41 +0300 Subject: [PATCH 2/2] Some improvements Mention type aliases Mention differences between unit structs / tuple struct constructors and `const` / `fn` items. Add more use cases for `TS{0: expr}`/`TS{0: pat}` --- text/0000-adt-kinds.md | 50 ++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/text/0000-adt-kinds.md b/text/0000-adt-kinds.md index e5894ea5509..30a4f10678e 100644 --- a/text/0000-adt-kinds.md +++ b/text/0000-adt-kinds.md @@ -43,7 +43,8 @@ Braced structs have 0 or more user-named fields and are defined only in type nam Braced structs can be used in struct expressions `S{field1: expr, field2: expr}`, including functional record update (FRU) `S{field1: expr, ..s}`/`S{..s}` and with struct patterns `S{field1: pat, field2: pat}`/`S{field1: pat, ..}`/`S{..}`. -In all cases the path `S` of the expression or pattern is looked up in the type namespace. +In all cases the path `S` of the expression or pattern is looked up in the type namespace (so these +expressions/patterns can be used with type aliases). Fields of a braced struct can be accessed with dot syntax `s.field1`. Note: struct *variants* are currently defined in the value namespace in addition to type namespace, @@ -63,7 +64,7 @@ Unit structs can be thought of as a single declaration for two things: a basic s struct US {} ``` -and a constant with the same name +and a constant with the same nameNote 1 ``` const US: US = US{}; @@ -74,15 +75,16 @@ constant `US`) namespaces. As a basic struct, a unit struct can participate in struct expressions `US{}`, including FRU `US{..s}` and in struct patterns `US{}`/`US{..}`. In both cases the path `US` of the expression -or pattern is looked up in the type namespace. +or pattern is looked up in the type namespace (so these expressions/patterns can be used with type +aliases). Fields of a unit struct could also be accessed with dot syntax, but it doesn't have any fields. As a constant, a unit struct can participate in unit struct expressions `US` and unit struct patterns `US`, both of these are looked up in the value namespace in which the constant `US` is -defined. +defined (so these expressions/patterns cannot be used with type aliases). -Note 1: the constant is not exactly a `const` item, there are subtle differences, but it's a close -approximation. +Note 1: the constant is not exactly a `const` item, there are subtle differences (e.g. with regards +to `match` exhaustiveness), but it's a close approximation. Note 2: the constant is pretty weirdly namespaced in case of unit *variants*, constants can't be defined in "enum modules" manually. @@ -103,7 +105,7 @@ struct TS { } ``` -and a constructor function with the same name +and a constructor function with the same nameNote 2 ``` fn TS(arg0: Type0, arg1: Type1, arg2: Type2) -> TS { @@ -117,17 +119,21 @@ and the value (the constructor function `TS`) namespaces. As a basic struct, a tuple struct can participate in struct expressions `TS{0: expr, 1: expr}`, including FRU `TS{0: expr, ..ts}`/`TS{..ts}` and in struct patterns `TS{0: pat, 1: pat}`/`TS{0: pat, ..}`/`TS{..}`. -In both cases the path `TS` of the expression or pattern is looked up in the type namespace. -Fields of a braced tuple can be accessed with dot syntax `ts.0`. +In both cases the path `TS` of the expression or pattern is looked up in the type namespace (so +these expressions/patterns can be used with type aliases). +Fields of a tuple struct can be accessed with dot syntax `ts.0`. As a constructor, a tuple struct can participate in tuple struct expressions `TS(expr, expr)` and tuple struct patterns `TS(pat, pat)`/`TS(..)`, both of these are looked up in the value namespace -in which the constructor `TS` is defined. Tuple struct expressions `TS(expr, expr)` are usual +in which the constructor `TS` is defined (so these expressions/patterns cannot be used with type +aliases). Tuple struct expressions `TS(expr, expr)` are usual function calls, but the compiler reserves the right to make observable improvements to them based on the additional knowledge, that `TS` is a constructor. -Note: the automatically assigned field names are quite interesting, they are not identifiers -lexically (they are integer literals), so such fields can't be defined manually. +Note 1: the automatically assigned field names are quite interesting, they are not identifiers +lexically (they are integer literals), so such fields can't be defined manually. +Note 2: the constructor function is not exactly a `fn` item, there are subtle differences (e.g. with +regards to privacy checks), but it's a close approximation. ## Summary of the changes. @@ -143,15 +149,21 @@ implement as well. This also means that `S{..}` patterns can be used to match structures and variants of any kind. The desire to have such "match everything" patterns is sometimes expressed given that number of fields in structures and variants can change from zero to non-zero and back during -development. +development. +An extra benefit is ability to match/construct tuple structs using their type aliases. New: Permit using tuple structs and tuple variants in braced struct patterns and expressions -requiring naming their fields - `TS{0: expr}`/`TS{0: pat}`/etc. This change looks a bit worse from -the cost/benefit point of view. There's not much motivation for it besides consistency and probably -shortening patterns like `ItemFn(name, _, _, _, _, _)` into something like `ItemFn{0: name, ..}`. -Automatic code generators (e.g. syntax extensions like `derive`) can probably benefit from the -ability to generate uniform code for all structure kinds as well. -The author of the RFC is ready to postpone or drop this particular extension at any moment. +requiring naming their fields - `TS{0: expr}`/`TS{0: pat}`/etc. +While this change is important for consistency, there's not much motivation for it in hand-written +code besides shortening patterns like `ItemFn(_, _, unsafety, _, _, _)` into something like +`ItemFn{2: unsafety, ..}` and ability to match/construct tuple structs using their type aliases. +However, automatic code generators (e.g. syntax extensions) can get more benefits from the +ability to generate uniform code for all structure kinds. +`#[derive]` for example, currently has separate code paths for generating expressions and patterns +for braces structs (`ExprStruct`/`PatKind::Struct`), tuple structs +(`ExprCall`/`PatKind::TupleStruct`) and unit structs (`ExprPath`/`PatKind::Path`). With proposed +changes `#[derive]` could simplify its logic and always generate braced forms for expressions and +patterns. # Drawbacks [drawbacks]: #drawbacks