Changeset 1b770e40
- Timestamp:
- Oct 6, 2024, 5:19:21 PM (2 hours ago)
- Branches:
- master
- Parents:
- b0fcd0e
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/proposals/tuples.md
rb0fcd0e r1b770e40 2 2 ====== 3 3 4 T his proposal is to update tuples, as they were created by Rob in his thesis,5 to address problems that have appeared in their use since then. This proposal 6 is an attempt to address some of those problems. Adding new functionality, 7 updating existing features and removing some problematic features. 8 9 The core of change is breaking the current restructurable tuples into 10 unstructured tuples and structured tuples. Unstructured tuples handle most 11 of the existing uses, with structured tuples filling in a few missing 12 use cases.4 Tuples were introduced by Dave Till in K-W C, added to CFA by Rodolfo Esteves, 5 and extended by Robert Schluntz. This proposal discusses updates for CFA tuples 6 to address problems that have appeared in their usage over the past 6 years. 7 The proposal attempts to address problems by adding new functionality, updating 8 existing features, and removing some problematic ones. 9 10 The core change is breaking the current restructurable tuples into unstructured 11 and structured tuples. Unstructured tuples handle most of the existing uses, 12 with structured tuples filling in a few missing use cases. 13 13 14 14 Current State of Tuples 15 15 ----------------------- 16 An overview of the current design, as the starting place for the proposal.16 An overview of the current tuples design is the starting place for the proposal. 17 17 18 18 ### Tuple Syntax 19 19 20 An overview of the syntax for the main three components of tuples, the types 21 of tuples, the tuple expressions/values (constructing tuples) and tuple index 22 expressions (deconstructing tuples). 23 24 Current syntax for tuple types: 20 Currently, tuples have three main components: tuple types, tuple 21 expressions/values (constructing tuples), and tuple index expressions 22 (deconstructing tuples). 23 24 Current syntax for tuple types. 25 25 26 - Nullary: [void] or [] 26 27 - Unary: [TYPE] 27 - Binary: [TYPE, TYPE] 28 - The pattern continues for three or more elements. 28 - Nary: [TYPE, TYPE, ...] 29 30 Tuple types can appear in a function return and parameter declaration, or a 31 tuple variable declaration. Note, the Nullary tuple is only meaningful in the 32 return context, 33 34 void f(...); // equivalent 35 [void] f(...); 36 [] f(...); 37 38 as C does not support a void declaration. 39 40 int f( void x ); // disallowed 41 int f( [void] x ); 42 int f( [] x ); 29 43 30 44 Current syntax for tuple expressions: 31 - Nullary: None. (Same as `void`, use return without an expression.) 32 - Unary: {EXPR} (This might be an error, but I can't make [EXPR] work.) 33 - Binary: [EXPR, EXPR] 34 - The pattern from binary continues for three or more elements. 35 36 Current syntax for tuple index expressions, is much simpler. It uses the 37 member index expression syntax, except the member name is replaced with the 38 element index, which is an integer literal, showing the index of the element 39 to get. 40 41 Here is a brief example showing all three parts. 45 46 - Nullary: (Same as `void`, use return without an expression.) 47 48 [] f( ) { return; } 49 [] f( ) { return [] ; } 50 51 - Unary: 52 53 [int] f( ) { return 3; } 54 [int] f( ) { return [3]; } 55 56 - Nary: [EXPR, EXPR] 57 58 [int,int] f( ) { return [3,4]; } 59 60 Currently, there is a parsing problem for nullary and unary tuple expression, 61 which is being looked at. Hence, only these two forms work. 62 63 [] f( ) { return; } // nullary 64 [int] f( ) { return 3; } // unary 65 66 Current syntax for tuple indexing is an integer constant, where its value 67 selects a tuple member, e.g.: 42 68 43 69 [char, int] tup; 44 70 tup = ['a', 0]; 45 int value = t.1; 71 char ch = t.0; // select first tuple member 72 int value = t.1; // select second tuple member 46 73 47 74 ### Mass and Multi-Assignment 48 75 49 Two special forms of assignment can be used to set values in tuples. 50 Mass Assignment assigns every element in the tuple to a single source value. 76 Two special forms of assignment can be used to set values in tuples: mass and 77 multi. Mass assignment assigns every element in the destination tuple to a 78 single source value. 51 79 52 80 [int, long, float] dst; 53 81 int src = 4 54 dst = src; 55 // Expands to roughly: dst.0 = src, dst.1 = src, dst.2 = src 56 57 Multi-Assignment assigns every element to the matching element of a source 58 tuple (both tuples must be the same size). 59 60 [int, long, float] dst; 61 [int, long, double] src = [1, 20, 300.0]; 62 dst = src; 63 // Expands to roughly: dst.0 = src.0, dst.1 = src.1, dst.2 = src.2 64 // Note that the last assignment is not an exact match, 65 // an implicit conversion will be applied. 82 dst = src; // => dst.0 = src; dst.1 = src; dst.2 = src 83 84 Multi-assignment assigns every element in the destination tuple to the 85 corresponding element in the source tuple. Both tuples must be the same size 86 and the elements assignment compatible => conversions. 87 88 [long, int, float] dst; 89 [int, char, double] src = [1, 'a', 300.0]; 90 dst = src; // => dst.0 = src.0; dst.1 = src.1; dst.2 = src.1 66 91 67 92 ### Tuple Restructuring 68 93 69 94 Tuples can be restructured as part of overload resolution. Function calls 70 will unpack tuples and repack tuples to match signatures. This is a type of 71 implicit conversion and is considered during overload resolution.95 unpack tuples and repack tuples to match signatures. This semantics is a form 96 of implicit conversion and is considered during overload resolution. 72 97 73 98 A simple example is matching multiple parameters of a function to a single 74 argument expression, each parameter is bound to a different element of the 75 returned tuple. 76 77 [int, int] paramFunc(); 78 void callFunc(int a, int b, int c, int d); 79 80 void restructure() { 81 callFunc(paramFunc(), paramFunc()); 99 argument expression, where each parameter is bound to a different element of 100 the returned tuple. 101 102 [int, int] argFunc(); 103 void parmFunc(int a, int b, int c, int d); 104 105 parmFunc(argFunc(), argFunc()); 106 107 // Roughly equivilent to: 108 [int, int] fst = argFunc(); 109 [int, int] snd = argFunc(); 110 parmFunc(fst.0, fst.1, snd.0, snd.1); 111 112 There are few languages supporting multiple return-values as a standalone 113 feature (SETL). Go has multiple return-values but restricts their use in 114 matching arguments to parameters. 115 116 func argFunc() (int, int) { 117 return 3, 7 82 118 } 83 // Roughly equivilent to: 84 void restructure() { 85 [int, int] fst = paramFunc(); 86 [int, int] snd = paramFunc(); 87 callFunc(fst.0, fst.1, snd.0, snd.1); 119 func parmFunc( a int, b int ) { 120 fmt.Println(a, b ) 88 121 } 89 90 This is the unique feature of Cforall tuples. There are a few languages with 91 multiple return values, but they are usually are a stand alone feature. 122 func main() { 123 parmFunc2( argFunc() ); // arguments must match exactly with parameters 124 } 92 125 93 126 ### Tuple Casts 94 127 95 128 C-style casts can be used on tuples. These are usually conversion casts (casts 96 that p reform operations on the cast type, as opposed to reinterpreting the129 that perform operations on the cast type, as opposed to reinterpreting the 97 130 existing value). 98 131 … … 103 136 ([int, char])x; 104 137 105 This casts the first element type from a char to an int and drops the last138 This casts the first element type of x from a char to an int and drops the last 106 139 element. The second line can be replaced with the following code, which creates 107 a new tuple by casting the kept elements of the old tuple:140 a new tuple by directly casting the kept elements of the old tuple: 108 141 109 142 [(int)x.0, (char)x.1]; … … 111 144 Note, tuple casting is more restricted than the implicit tuple restructuring. 112 145 It cannot do any restructuring beyond dropping the trailing elements of 113 tuples. For example, you cannot cast `[int, [bool, char], int]` to be a 114 `[int, bool, char, int]`. 146 tuples. For example, 147 148 int i; char c; bool b; 149 [i, b, c, i] = [i, [b, c], i]; 150 [int, [bool, char], int] w; 151 [i, b, c, i] = w; 152 [i, b, c, i] = ([int, bool, char, int])w; // fails 153 [i, b, c] = ([int, [bool, char]])w; // works 115 154 116 155 ### Polymorphic Tuple Parameters … … 121 160 sequence of types. 122 161 123 forall(T, Ts... | { T max(T, T); T max(Ts); }) 162 forall( T | { int ?>?( T, T ); } ) 163 T max( T v1, T v2 ) { return v1 > v2 ? v1 : v2; } // base case 164 165 forall(T, Ts... | { T max(T, T); T max(Ts); }) // recursive case 124 166 T max(T arg, Ts args) { 125 167 return max(arg, max(args)); 126 168 } 127 169 128 This introduces a type name into scope. It is used as a type but because the 129 tuple is flattened, the second assertion "T max(Ts);" matches types with 130 multiple parameters, although it is used as a tuple function inside the 131 function body. For example, in the three-argument case (all three of the 132 same type), both assertions can match the same function. Then "Ts args" 133 introduces args as a tuple, where it is passed to max. 170 This feature introduces a type name into scope (Ts). It is used as a type but 171 because the tuple is flattened, the second assertion "T max(Ts);" matches types 172 with multiple parameters (the `...`), although it is used as a tuple function 173 inside the function body (max(args)). 174 175 The first non-recursive max function is the polymorphic base-case for the 176 recursion, i.e., find the maximum of two identically typed values with a 177 greater-than (>) operator. The second recursive max function takes two 178 parameters, a T and a Ts tuple, handling all argument lengths greater than two. 179 The recursive function computes the maximum for the first argument and the 180 maximum value of the rest of the tuple. The call of max with one argument is 181 the recursive call, where the tuple is converted into two arguments by taking 182 the first value (lisp car) from the tuple pack as the first argument 183 (flattening) and the remaining pack becomes the second argument (lisp cdr). 184 The recursion stops when the tuple is empty. For example, max( 2, 3, 4 ) 185 matches with the recursive function, which performs return max( 2, max( [3, 4] 186 ) ) and one more step yields return max( 2, max( 3, 4 ) ), so the tuple is 187 empty. 188 134 189 135 190 Issues with the Current State 136 191 ----------------------------- 137 There are a variety of problems with the current implementation which we138 would like to fix.192 There are a variety of problems with the current implementation which need to 193 be fixed. 139 194 140 195 ### Tuples are not Objects 141 196 142 Spoilers, this proposal actually takes them further away from being objects. 143 But this illustrates why the current version is not as useful is it could be. 144 145 Tuples do not have the lifetime operators (copy construction, copy assignment 146 and destructor) and cannot be passed in as a bundle to polymorphic functions. 147 The multi-assignment operation is not a single function and is not passed 148 in as an assertion. 149 150 This prevents tuples from being interwoven with regular polymorphic code. 197 Spoilers: this proposal actually takes them even further away from being 198 objects, but illustrates why the current version is not as useful is it could 199 be. 200 201 Because of the fluid nature of tuples (flattening/structuring), routines like 202 constructions, destructor, or assignment do not make sense, e.g. this 203 constructor matches multiple a tuple types: 204 205 void ?{} ( [int, int, int] ? this ); 206 [int, [int, int]] x; // all match constructor type by flattening 207 [int, int, int] y; 208 [[int, int], int] z; 209 210 as could a similarly typed destructor or assignment operator. This prevents 211 tuples from being interwoven with regular polymorphic code. 151 212 152 213 ### Providing TType Arguments is Inconsistent 153 214 154 The syntax for ttype arguments is slightly inconsistent. It has n't come up155 much yet because you do not directly provide ttype polymorphic arguments to215 The syntax for ttype arguments is slightly inconsistent. It has not come up 216 much yet, because you do not directly provide ttype polymorphic arguments to 156 217 functions and there are very few existing use-cases for ttype structures. 157 218 158 Passing arguments to a function inlines the arguments ,219 Passing arguments to a function inlines the arguments 159 220 while passing them to a polymorphic type requires them to be 160 221 enclosed in a tuple. Compare `function(x, y, z)` with `Type(A, [B, C])`. 161 222 162 This did not come up previously as there was little reason to explicit y223 This did not come up previously as there was little reason to explicitly 163 224 provide ttype arguments. They are implicit for functions and there is very 164 225 little use case for a ttype on a struct or union. … … 166 227 ### Syntax Conflict 167 228 168 The tuple syntax conflicts with designators and the new attribute syntax. 169 These conflicts break C compatibility goals of Cforall. Designators have had 170 to their syntax change and Cforall cannot parse the new attributes. 171 172 Although most of this redesign is about the semantics of tuples, but an 173 update to tuple syntax that removes these conflicts that would improve the 174 compatibility of Cforall going forward (and also open up the new attribute 175 syntax for cforall features). 229 The tuple syntax conflicts with designators and the new C++-style attribute 230 syntax. 231 232 struct S { int a[10]; } = { [2] = 3 }; // [2] looks like a tuple 233 [[ unused ]] [[3, 4]]; // look ahead problem 234 235 These conflicts break C compatibility goals of Cforall. Designators had to 236 their syntax change and Cforall cannot parse the new attributes. 237 238 Although most of this redesign is about the semantics of tuples, but an update 239 to tuple syntax that removes these conflicts would improve the compatibility of 240 Cforall going forward (and also open up the new attribute syntax for cforall 241 features). 176 242 177 243 Change in Model … … 181 247 struct tuple is added to cover those cases. 182 248 183 The existing tuples will be even more "unstructured" than they are now.184 Tuples are considered a sequence of types or typed entities. These packs are 185 then unpacked into the surrounding context. Put differently, tuples are now 186 flattened as much as possible, with some places (like parameter lists) being 187 treated as animplicit tuple and the tuple being flattened into that.188 189 Structured tuples are now a separate feature ,a structure called "tuple".190 These are polymorphic structures; an instance should act as a structure, 191 except that it uses indices instead of field names. These structures shouldn't 192 have to be used often, but fill in the use cases that unstructured tuples 193 no longer support.249 The new tuples is even more "unstructured" than before. New tuples are 250 considered a sequence of types or typed entities. These packs are then unpacked 251 into the surrounding context. Put differently, tuples are now flattened as much 252 as possible, with some places (like parameter lists) being treated as an 253 implicit tuple and the tuple being flattened into that. 254 255 Structured tuples are now a separate feature: a structure called "tuple". 256 These are polymorphic structures; an instance should act as a structure, except 257 its fields are accessed using indices instead of field names. Experience so far 258 is that structured tuples are not used often, but fill in the use cases that 259 unstructured tuples no longer support. 194 260 195 261 Note that the underlying implementation might not actually look like this. … … 201 267 ### Structured Tuple Type 202 268 203 There are still use cases for a structured tuple type. The issues in 204 supporting both were because there was one feature trying to support both 205 uses. Hence, it is added back in as its own feature. 206 207 There should be a standard library or built-in type named `tuple`, it doesn't 208 need a special syntax to write types or instances. The type definition might 209 use some primitive support, but if supported as a regular type would look 210 something like this: 269 There is a standard library or built-in type named `tuple`, it does not need a 270 special syntax to write types or instances. The type definition might need some 271 primitive support, but if supported as a regular type would look something like 272 this: 211 273 212 274 forall(Ts...) 213 275 struct tuple { 214 inline Ts all; 276 inline Ts all; // inline all specified fields 215 277 }; 216 278 217 This will beconstructed the same way as most types, a list initializer with218 each tuple argument, and the lifetime functions (co py construction, copy219 assignment and destruction) work the same. Field access works two ways, the 220 first is accessing the all field, effectively converting the structured 221 "struct" tuple into an unstructured tuple, the other is to use tuple indexing 222 directly on the structure as if it was an unstructured tuple.223 224 (If inline doesn't work, just rename all to `get`. It does make things a bit225 longer but has no change in functionality. If the all access doesn't work,226 that is a bit more of a problem,tuple slicing might provide a work around.)279 This type is constructed the same way as most types, a list initializer with 280 each tuple argument, and the lifetime functions (construction, assignment and 281 destruction) work the same. Field access works two ways, the first is accessing 282 the `all` field, effectively converting the structured tuple into an 283 unstructured tuple, the other is to use tuple indexing directly on the 284 structure as if it is an unstructured tuple. 285 286 (If `inline` does not work, just rename all to `get`. It does make things a bit 287 longer but has no change in functionality. If the `all` access does not work, 288 that is more problematic, and tuple slicing might provide a work around.) 227 289 228 290 ### Type Qualifier Distribution … … 233 295 the tuple. 234 296 235 Previously `[int, bool] &` would mean a reference to a tuple of an integer 236 and a boolean. Now it would be an alias for `[int &, bool &]`, a tuple of a 237 reference to an integer and a reference to a boolean. This also applies to 238 polymorphic tuple type packs `Ts &` in polymorphic functions. 239 240 This allows to specify restrictions on types as you would with a single 241 type variable. For example, this can help replace the special cased 242 tuple operations, multi-assignment (N-to-N) and mass-assignment (1-to-N). 297 Previously `const [int, bool] &` would mean a const reference to a tuple of an 298 integer and a boolean. Now it means an alias for `[const int &, const bool &]`, 299 a tuple of a reference to a constant integer and a reference to a constant 300 boolean. This also applies to polymorphic tuple type packs `Ts &` in 301 polymorphic functions. 302 303 This new approach can specify restrictions on tuple variables as for a single 304 type variable. For example, this approach can replace the special cased tuple 305 operations multi-assignment (N-to-N) and mass-assignment (1-to-N). 243 306 244 307 // Multi-Assignment … … 269 332 ### Type Packs 270 333 271 This is not a new feature, but a reframing/extension of existing tupletuple272 polymorphic parameters as polymorphic type packs. The `Vars...` syntax 273 introduces a pack of types into scope. It can be used in many ofthe same274 way sas a tuple, but in some new ways to.275 276 The primary existing use remains ; you can use a polymorphic pack in a277 parameter list, both as part of an assertion and in the signature of the 278 main function. The difference is that this is not an enclosed tuple, but 279 a series of types. The only effective difference this makes is it doesn't 280 prefer to matchanother tuple/pack.334 This approach is not a new feature, but a reframing/extension of existing tuple 335 tuple polymorphic parameters as polymorphic type packs. The old `Vars...` 336 syntax introduces a pack of types into scope. It can be used in much the same 337 way as a tuple, but in some new ways to. 338 339 The primary existing use remains: to use a polymorphic pack in a parameter 340 list, both as part of an assertion and in the signature of the main 341 function. The difference is that this is not an enclosed tuple, but a series of 342 types. The only effective difference this makes is it does not prefer to match 343 another tuple/pack. 281 344 282 345 This pattern continues to a parameter defined with a pack of types, which … … 287 350 void function(Params values); 288 351 289 New use cases include declarations of members and variables. For example, 290 the creation thestructured tuple structure:352 New use cases include declarations of members and variables. For example, the 353 creation of a structured tuple structure: 291 354 292 355 forall(Fields...) … … 320 383 321 384 Declaring a tuple acts as a pack of variable declarations. When this is done 322 with written out type (as opposed to a polymorphic parameter above), then the385 with a written out type (as opposed to a polymorphic parameter above), then the 323 386 elements of the tuple can be named. 324 387 … … 333 396 no named to access it. 334 397 398 PAB: I do understand the point of this. 399 335 400 ### Tuple Casts 336 401 … … 344 409 The unstructured tuple cannot represent all the types that the previous 345 410 semi-structured tuple could. These cases still exist in various ways, 346 speci alin the internals of a polymorphic type, but in general should be411 specifically in the internals of a polymorphic type, but in general should be 347 412 considered in their reduced form. 348 413 … … 350 415 conflicts that currently exist. 351 416 352 Nullary, or 0 element tuples, are equiv lent to void, the type that carries417 Nullary, or 0 element tuples, are equivalent to void, the type that carries 353 418 no data, because they do not either. It should either be replaced with void 354 419 or removed entirely when it appears in a larger sequence. 355 420 356 421 // For example, this function: 357 forall(Ts...) Ts example(int first 422 forall(Ts...) Ts example(int first, ????) 358 423 // With Ts = [], should not be treated as: 359 424 [] example(int first, [] middle, int last); … … 364 429 is to say a single type in an unstructured tuple is a no-op. 365 430 366 Lastly, nested tuples are always flattened into to form a one-deep tuple.367 This means that `[bool, [char, int], float]` is resolved as 368 `[bool, char, int, float]`,with the internal structure of the tuple ignored.369 370 The flatten into a large sequence rule mentioned above ,is actually just an371 application of this. Unstructured tuples can already be restructured, even 372 at the top level of an function call. This can be expressed by considering 373 theargument list as a tuple:431 Lastly, nested tuples are always flattened to a one-depth tuple. This means 432 that `[bool, [char, int], float]` is resolved as `[bool, char, int, float]`, 433 with the internal structure of the tuple ignored. 434 435 The flatten into a large sequence rule mentioned above is actually just an 436 application of this. Unstructured tuples can already be restructured, even at 437 the top level of an function call. This can be expressed by considering the 438 argument list as a tuple: 374 439 375 440 call(false, ['a', -7], 3.14) … … 385 450 (Disclaimer: this is a bit more impulsive, see end for notes.) 386 451 387 This is an extension to tuple indexing. Currently, you may index single 388 location of a tuple, extracting the element at that location. By extending 389 the index expression to be a range we can grab a slice of the existing tuple 390 as another smaller tuple. 391 392 [int_x, char_y] = [int_a, char_b, double_c].(0+~=1) 393 394 To get the new tuple, the range has to be constructed and traversed at 395 compile time. The size of the range is the size of the result tuple, with 396 each element in the result tuple decided by the matching element of the 397 range, which gives the index of the original tuple to place there. 398 The type of the indices may be and integral type, but all indices must be in 399 range, otherwise it is a compile time error. 452 This is an extension to tuple indexing. Currently, only single location of a 453 tuple can be index, extracting the element at that location. By extending the 454 index expression to be a list-range a multiple sub-tuples can be extracted. 455 456 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].0~2,5~9~2 457 458 produces the tuple 459 460 [0, 1, 2, 5, 7, 9] 461 462 (Note the position and values are the same to simplify the example.) That is, 463 index the first 3 tuple elements, and then indexes elements 5 to 9 in steps of 464 2. Not all selections are possible with this mechanism (e.g., index the 465 Fibonacci elements), but it covers many cases. 466 467 To get the new tuple, the range has to be constructed and traversed at compile 468 time. The size of the range is the size of the result tuple, with each element 469 in the result tuple decided by the matching element of the range, which gives 470 the index of the original tuple to place there. The type of the indices may be 471 an integral type, but all indices must be in range, otherwise it is a compile 472 time error. 400 473 401 474 In terms of the existing range loops, if you could write this dynamically, it … … 410 483 as indexing the single value in the range. 411 484 412 Some closing notes, this is dependent on generali sed range expressions.485 Some closing notes, this is dependent on generalized range expressions. 413 486 The iterators proposal has a section on some new range types, this feature 414 487 is intended to be built on that feature. Not simply reuse some of the syntax … … 416 489 need to be improved. 417 490 491 PAB, I don't understand this last part as the index range is compile time not 492 runtime. 493 418 494 Implementation 419 495 -------------- … … 421 497 422 498 ### Structured Tuple Implementation 423 Under the hood, unstructured tuples will probably just be implemented as 424 structured tuples, with the restructuring code inserted wherever needed. 425 In short, the base implementation should stay mostly the same. 499 500 Under the hood, unstructured tuples are implemented as structured tuples, with 501 the restructuring code inserted wherever needed. In short, the base 502 implementation should stay mostly the same. 503 504 PAB: The current implementation does not use convert unstructured tuples to 505 structured tuples. Look at the code generated for 506 507 int x, y; 508 [x, y] = 3; 509 [x, y] = [y, x]; 510 426 511 427 512 Actually inlining tuples can be done in some cases, it may even help with … … 431 516 432 517 ### AST Updates 518 433 519 The current AST cannot represent all the new features. Particularly, an 434 520 object declaration cannot name elements of the tuple. To this end a new … … 438 524 This would act much like a `FunctionDecl` except for tuples, narrowing the 439 525 possible types, to `TupleType` instances instead of `FunctionType` instances, 440 and storing some additional information , in this casethe names of the526 and storing some additional information. In this case, the names of the 441 527 elements of the tuples. 528 529 PAB: the following parses: 530 531 [int x, int y] foo( int p ); 532 533 and discussed by Till. 442 534 443 535 (I'm not actually going to decide the implementation now, but some early … … 446 538 447 539 ### Field Packs 448 Field packs in structures will probably have to be written out in full by 449 the specialization pass. If they are not, it could have some negative effects 450 on layout, causing a structure to take up extra space. It may be able to 451 reuse some of the specialization code for the existing tuples. 540 541 Field packs in structures probably have to be written out in full by the 542 specialization pass. If not, it could have some negative effects on layout, 543 causing a structure to take up extra space. It may be able to reuse some of the 544 specialization code for the existing tuples. 452 545 453 546 Related Features in Other Languages … … 461 554 There are many languages with structured tuples. Usually just called tuples, 462 555 but they usually follow the same rules as a polymorphic anonymous structures, 463 that is to say you provide N typesto create a new N-arity tuple. They also556 that is to say N types are provided to create a new N-arity tuple. They also 464 557 usually have some special syntax to index the tuples, because there are no 465 field names to use.558 field names. 466 559 467 560 #### Rust 561 468 562 Rust has the standard tuples as a primitive in the language. Each arity of 469 563 tuple is a different polymorphic type. … … 483 577 484 578 Some tuple features only apply up to 12-arity tuples. 485 It is not directly stated, but I believe the difference i sthe more limited486 features are implemented in the standard library, for each arity of tuple.579 It is not directly stated, but I believe the difference in the more limited 580 features are implemented in the standard library, one for each arity of tuple. 487 581 488 582 - https://doc.rust-lang.org/std/primitive.tuple.html … … 490 584 - https://doc.rust-lang.org/reference/expressions/tuple-expr.html 491 585 492 #### C++ 586 #### C++ tuple 587 493 588 Implemented as a template type defined in the standard library. No special 494 589 language features exist to support this, due to the power of C++'s template … … 500 595 types. This is the standard notation for template type instances. 501 596 502 There is no special syntax for member access of a tuple. You have to use a503 template function, for example`std::get<0>( tuple )`.597 There is no special syntax for member access of a tuple. A template function is 598 used, e.g., `std::get<0>( tuple )`. 504 599 505 600 C++ also has structured binding, a kind of limited pattern matching. In a … … 511 606 - https://en.cppreference.com/w/cpp/language/structured_binding 512 607 513 #### Haskell 514 Haskell has a special syntax for tuples, but otherwise they are completely 515 normal polymorphic types. Because of this, tuples have a maximum size. 516 Haskell (98) supports tuples of up to 15 elements and the standard library 517 has functions for tuples of up to 7 elements. 518 519 The syntax for tuples is a comma separated list of elements. Either element 520 types to create a tuple type, or element values to create a tuple value. The 521 context decides which one we are looking for. Such as `(6, "six")` or 522 `(Bool, Char, Int)`. 523 524 You can also remove all the elements, getting an expression like "(,)" or 525 "(,,,)", which can be then be used as a function, for a type or expression. 526 527 Haskell supports pattern matching as the main way to extract values from a 528 tuple, although helper functions "fst" and "snd" are provided for field 529 access on two element tuples. 530 531 Haskell does not have 0 or 1-element tuples. The nil type, written "()" for 532 both type and value, is effectively the 0 element tuple. There is also a type 533 called "Solo" that is a polymorphic structure with one field and is used as 534 the 1-element tuple, but has regular Haskell data type syntax. 535 536 - https://www.haskell.org/onlinereport/basic.html 537 538 #### OCaml 539 OCaml only supports multi-element (2 or more) tuples. Tuple types are written 540 as a '*' separated list of types, tuple values are written as ',' separated 541 list of expressions. Pattern matching tuples is supported and uses the same 542 syntax as values. Parenthesizing these lists is only needed for grouping. 543 544 OCaml does not support 0 or 1 element tuples. It does however have the `unit` 545 type which has one value, written `()`. 546 547 - https://ocaml.org/docs/basic-data-types#tuples 548 549 #### Swift 550 Swift has tuple types that use the basic parenthesized, comma 551 separated list of types or values. It only supports 0 and 2 or more element 552 tuples (the `Void` type is an alias for the empty tuple type). 553 554 Swift also supports named tuples. Names can be added to before the tuple 555 element, both for the tuple type and value. The syntax is a name followed by 556 a colon, for example `(first: int, second: int)`. These names are a fixed 557 part of the type, and can be used as part of field access notation (otherwise 558 numbers are used in-place of field names `tuple.0` vs. `tuple.first`). 559 560 - https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types/#Tuple-Type 561 562 #### Python 563 In Python tuples are immutable lists. Because they are dynamically typed 564 there is only one tuple type `tuple`. 565 566 It also has various named tuples. The first, namedtuple, just allows you to 567 add a name the elements of the tuple. The second, NamedTuple, is actually a 568 way of creating a typed record in a normally untyped language. 569 570 - https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range 571 - https://docs.python.org/3/library/collections.html#collections.namedtuple 572 - https://docs.python.org/3/library/typing.html#typing.NamedTuple 573 574 #### LISP 575 As LISP is dynamically typed, its cons data type is an untyped pair and is 576 (perhaps infamously) the main constructor of all compound data types. 577 The function `cons` takes two arguments and builds a pair. Functions `car` 578 and `cdr` get the first and second elements of the pair. 579 580 ### Packs 581 582 Packs (or unstructured tuples) are a much less common feature. In fact, there 583 might just be one language, C++, that supports packs. The most common use 584 case for unstructured tuples is returning multiple values, so there is a 585 comparison to languages that have that as a special feature. 586 587 #### Go 588 Go does not have built in tuple types, but it has multi-return syntax that 589 looks like the tuple syntax of many other languages. 590 591 ``` 592 func main() { 593 i, j := returnIntInt() 594 ... 595 } 596 597 func returnIntInt() (int, int) { 598 return 12, 34 599 } 600 ``` 601 602 - https://golangdocs.com/functions-in-golang 603 - https://go.dev/src/go/types/tuple.go 604 605 #### Lua 606 Lua is a scripting language, is dynamically typed and stack based. Although 607 the stack usually only directly visible in the C-API, it does allow any 608 function to return any number of values. Even in a single return, if the 609 return expression 610 611 ``` 612 local funcion f() 613 return 12, 34 614 end 615 616 local i, j = f() 617 ``` 618 619 #### C++ 608 PAB: I do not understand the syntax `auto [first, second]`. Where does it come 609 from? 610 611 #### C++ template 612 620 613 C++ templates can take various types of parameters, including parameter 621 614 packs. These contain series of values. These are used in pack expansion, … … 659 652 - https://en.cppreference.com/w/cpp/language/parameter_pack 660 653 - https://en.cppreference.com/w/cpp/language/fold 654 655 #### Haskell 656 657 Haskell has a special syntax for tuples, but otherwise they are completely 658 normal polymorphic types. Because of this, tuples have a maximum size. 659 Haskell (98) supports tuples of up to 15 elements and the standard library 660 has functions for tuples of up to 7 elements. 661 662 The syntax for tuples is a comma separated list of elements. Either element 663 types to create a tuple type, or element values to create a tuple value. The 664 context decides among them, such as `(6, "six")` or `(Bool, Char, Int)`. 665 666 Also all the elements can be removed, getting an expression like "(,)" or 667 "(,,,)", which can be then be used as a function, for a type, or an expression. 668 669 Haskell supports pattern matching as the main way to extract values from a 670 tuple, although helper functions "fst" and "snd" are provided for field 671 access on two element tuples. 672 673 Haskell does not have 0 or 1-element tuples. The nil type, written "()" for 674 both type and value, is effectively the 0 element tuple. There is also a type 675 called "Solo" that is a polymorphic structure with one field and is used as 676 the 1-element tuple, but has regular Haskell data-type syntax. 677 678 - https://www.haskell.org/onlinereport/basic.html 679 680 #### OCaml 681 682 OCaml only supports multi-element (2 or more) tuples. It does have the `unit` 683 type, which has one value, written `()`. Tuple types are written as a '*' 684 separated list of types, tuple values are written as ',' separated list of 685 expressions. Pattern matching tuples is supported and uses the same syntax as 686 values. Parenthesizing these lists is only needed for grouping. 687 688 - https://ocaml.org/docs/basic-data-types#tuples 689 690 #### Swift 691 692 Swift has tuple types that use the basic parenthesized, comma separated list of 693 types or values. It only supports 0 and 2 or more element tuples (the `Void` 694 type is an alias for the empty tuple type). 695 696 Swift also supports named tuples. Names can be added before the tuple element, 697 both for the tuple type and value. The syntax is a name followed by a colon, 698 e.g., `(first: int, second: int)`. These names are a fixed part of the type, 699 and can be used as part of field access notation (otherwise numbers are used 700 in-place of field names `tuple.0` vs. `tuple.first`). 701 702 - https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types/#Tuple-Type 703 704 #### Python 705 706 In Python tuples are immutable lists. Because they are dynamically typed, 707 there is only one tuple type `tuple`. 708 709 It also has various named tuples. The first, namedtuple, allows naming the 710 elements of the tuple. The second, NamedTuple, is actually a way of creating a 711 typed record in a normally untyped language. 712 713 - https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range 714 - https://docs.python.org/3/library/collections.html#collections.namedtuple 715 - https://docs.python.org/3/library/typing.html#typing.NamedTuple 716 717 #### LISP 718 As LISP is dynamically typed, its `cons` data type is an untyped pair and is 719 (perhaps infamously) the main constructor of all compound data types. The 720 function `cons` takes two arguments and builds a pair. Functions `car` and 721 `cdr` get the first and second elements of the pair. 722 723 ### Packs 724 Packs (or unstructured tuples) are a much less common feature. In fact, there 725 might just be one language, C++, that supports packs. The most common use 726 case for unstructured tuples is returning multiple values, so there is a 727 comparison to languages that have that as a special feature. 728 729 #### Go 730 Go does not have built in tuple types, but it has multi-return syntax that 731 looks like the tuple syntax of many other languages. 732 733 ``` 734 func main() { 735 i, j := returnIntInt() 736 ... 737 } 738 739 func returnIntInt() (int, int) { 740 return 12, 34 741 } 742 ``` 743 744 - https://golangdocs.com/functions-in-golang 745 - https://go.dev/src/go/types/tuple.go 746 747 #### Lua 748 Lua is a scripting language that is dynamically typed and stack based. Although 749 the stack is usually only directly visible in the C-API, it does allow any 750 function to return any number of values, even a single return, in the return 751 expression 752 753 ``` 754 local funcion f() 755 return 12, 34 756 end 757 758 local i, j = f() 759 ```
Note: See TracChangeset
for help on using the changeset viewer.