Re: A Discussion of Tuple Syntax
On 2013-08-21 16:59:17 +, Andrei Alexandrescu said: On 8/20/13 8:25 PM, Michel Fortin wrote: On 2013-08-21 00:38:30 +, Andrei Alexandrescu said: 1. What do we need? I think D needs less tuples. It has two kinds (language kind and Phobos's kind), which is confusing. It should have just one that covers most use cases. Yah, fewer tuples (or assigning distinct names to what we call today such) would be awesome. That seems like the only thing everyone agrees with. :-) I'm unfortunately lost already. I was discussing tuples as in "anonymous structs", not as in "template tuples". Well, the original post that started this discussion talked about both. I don't think narrowing it only to "anonymous structs" is going to solve the syntax problem as a whole. int a, b, c, d, e, f; swap(...(a, c, e), ...(b, d, f)); This looks like an example taken from a book in which "..." means some stuff is omitted. Seriously, the major issue with tuples is a conceptual one. No pretty syntax is going to fix it alone. Unfortunately, talking about concepts requires inventing a syntax for them. The syntax then immediately get dismissed as ugly/impractical and no thought is given to what's under it. No wonder this is getting nowhere for the nth time when everyone thinks so superficially. Just disregard the "..." syntax. It makes sense in the context of my other post. There's no way to appreciate it without that context (and even then, it can surely be improved). The idea (in my other post) was to un-cripple language-level tuples with one simple fundamental change: allow them to be packed and expanded. With that you can cover 99% of what you want from a tuple struct using the built-in language-level tuple. Plus you can do some other things like the swap of aliases to variables shown above. There is no reason for template-argument-tuples to be auto-expanding. If you fix that, as I proposed in my other post, you'll almost never need a library struct template to represent a tuple anymore. Thus, fewer tuples. -- Michel Fortin michel.for...@michelf.ca http://michelf.ca
Re: A Discussion of Tuple Syntax
On 8/20/2013 1:10 PM, Jonathan M Davis wrote: On Tuesday, August 20, 2013 13:06:27 H. S. Teoh wrote: On Tue, Aug 20, 2013 at 11:53:50AM -0700, Andrei Alexandrescu wrote: Somebody shoot me. [...] BANG! Don't kill him. He hasn't finished the allocators yet! ;) Don't worry, we'll keep sliding food under his door!
Re: A Discussion of Tuple Syntax
Zach the Mystic: I agree. The negative space *around* the bikeshed should be used... Andrei missed the point of that part of the discussion. That was not an explanation of the usefulness of wildcards, it was a small explanations of why probably you can't use '_' as wildcard symbol. The examples I have shown were not meant to show why wildcards are very useful in a rounded tuple design. auto (void, void, x) = t1; (Just an idea I had. Don't know whether it's technically sound. It just seemed so funny to me that the idea popped into my head as soon as you said "rainbow". Not trying to waste time. ) That is perhaps technically acceptable, but it's longer. Bye, bearophile
Re: A Discussion of Tuple Syntax
H. S. Teoh: This is adequately taken care of by std.typecons.Tuple, isn't it? You can't make 'i' a runtime value because D is a statically-typed language. Yep. (From several persons explaining me similar things in answers to my posts it seems I am not expressing well that I know what I am discussing about. In future I will add notes to avoid this). Currently you could do: Tuple!(...) t1; auto a = t1[0]; auto b = t1[1]; Not as nice, certainly, but I wouldn't consider it a deal-breaker. Currently we have: auto mult = (Tuple!(int,int) t) => t[0] * t[1]; Not as pretty, but surely still readable and usable? Or am I missing something? I didn't look in detail at your red-black tree example, or the Huffman encoding example, but it would seem that currently, std.typecons.Tuple more-or-less suffices for your needs, right? Except for some syntactic unpleasantness, that is. Or is there something that *can't* be expressed in an adequate way by the current Tuple that I missed? See what I wrote at the beginning of the post: We can live fine without a syntax to manage tuples, so strictly speaking we need nothing The semantics of tuples is simple and everything you can do when them is very easy to do in a language like C. The point of having tuples is to write code that is more readable, shorter, more expressive, a bit higher level. std.typecons.Tuple misses some expressiveness that some programmers think is handy to have, that I have listed in my post. What should happen if s is a runtime variable that may not have the same number of words as the number of elements in the tuple on the left-hand side? A runtime error? Again, what should happen if at runtime the lines have more or less elements than the tuple on the left-hand side? A runtime error? The answer I wrote in another post: Runtime boundary checks and tests are not needed if you unpack a fixed-sized array: auto tup = Tuple!(int, string[2])(1, ["red", "blue"]); auto {x, [c1, c2]} = tup; Regarding the de-structuring of dynamic arrays, I think it's not too much different than this: void main() { int[] a = [10, 20, 30]; int[2] b = a; } If you compile and run it without switches, you get at run-time: object.Error: lengths don't match for array copy, 2 = 3 While I receive no run-time errors if I compile with -noboundscheck. Isn't this an auto-expanding tuple that Andrei didn't like? I don't know. Ask to him. Bye, bearophile
Re: A Discussion of Tuple Syntax
On Wednesday, 21 August 2013 at 03:39:32 UTC, Jonathan M Davis wrote: But I see no reason to try and combine any of that with Tuple, as Tuple and TypeTuple do fundamentally different things. TypeTuple does not cover all built-in tuple cases. There is also built-in expression/run-time tuple which is also native to language but differs from library std.typecons.Tuple _only_ with lack of ABI. You may underestimate the mess here.
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 20:46:02 UTC, Andrei Alexandrescu wrote: void main() { auto t1 = #(5, "hello", 1.5); auto (?, ?, x) = t1; auto (?, gr, ?) = t1; } Bye, bearophile It's stuff like this that's just useless and gives a bad direction to the whole discussion. There's hardly anything wrong with auto x = t1[2] or auto gr = t1[1], but once the bikeshed is up for painting, the rainbow won't suffice. Andrei I agree. The negative space *around* the bikeshed should be used... auto (void, void, x) = t1; (Just an idea I had. Don't know whether it's technically sound. It just seemed so funny to me that the idea popped into my head as soon as you said "rainbow". Not trying to waste time. )
Re: A Discussion of Tuple Syntax
On Wed, Aug 21, 2013 at 03:43:45AM +0200, bearophile wrote: > Andrei Alexandrescu: > > >1. What do we need? > > We can live fine without a syntax to manage tuples, so strictly > speaking we need nothing (almost, I'd like to kill one currently > accepted syntax, see below). > > But when you write D in a high-level style, and you are used to > other functional or scripting languages, in several cases you desire > a more handy/compact syntax to manage tuples. > > Tuples are simple data structure, so the operations you want to do > with them are few and simple: > - To define a tuple of various fields of different type, that is a > tuple literal, to create a tuple; This is adequately taken care of by std.typecons.Tuple, isn't it? > - A way to read and write items of a tuple (unless it's read-only), > D uses a nice syntax [i] as arrays indexing, but i can't be a > run-time value. You can't make 'i' a runtime value because D is a statically-typed language. The compiler has to know at compile-time what type tup[i] is. Otherwise it couldn't generate machine code for things like: void func(Tuple t, int i) { auto x = t[i]; // what's the type of x? } > - With the Phobos tuple we are used to giving names to fields. It's > handy, but it's not so necessary if you have a way to assign tuple > items to variables, defined in-place. There are several places where > pulling apart a tuple in that way is useful, here I use a basic > syntax to be more clear: > > Assignments: > auto (a, b) = t1; Currently you could do: Tuple!(...) t1; auto a = t1[0]; auto b = t1[1]; Not as nice, certainly, but I wouldn't consider it a deal-breaker. > In function signatures: > auto mult = ((int x, int y)) => x * y; Currently we have: auto mult = (Tuple!(int,int) t) => t[0] * t[1]; Not as pretty, but surely still readable and usable? Or am I missing something? > In foreach loops: > > void main() { > import std.range, std.stdio; > auto a = [10, 20]; > auto b = [100, 200]; > > // Currently D supports this syntax: > foreach (x, y; zip(a, b)) > writeln(x, " ", y); > > // But it's unreliable, now x is the index of the > // array instead of the first tuple fields, so I > // think this feature of D should be killed as > // soon as possible: > foreach (x, y; zip(a, b).array) > writeln(x, " ", y); > } Isn't this an auto-expanding tuple that Andrei didn't like? Though granted, the current way of expressing it is not as nice: foreach (t; zip(a,b)) { writeln(t[0], " ", t[1]); } [...] > There are few more little pieces that are useful, like unpacking an > array: > > string s = "red blue"; > auto (col1, col2) = s.split; What should happen if s is a runtime variable that may not have the same number of words as the number of elements in the tuple on the left-hand side? A runtime error? [...] > For an use case of the assignment from an array, let's say you have > a text file containing two numbers in a line, that you want to read: > > const (x, y) = filein.byLine.front.split.to!(int[]); [...] Again, what should happen if at runtime the lines have more or less elements than the tuple on the left-hand side? A runtime error? I didn't look in detail at your red-black tree example, or the Huffman encoding example, but it would seem that currently, std.typecons.Tuple more-or-less suffices for your needs, right? Except for some syntactic unpleasantness, that is. Or is there something that *can't* be expressed in an adequate way by the current Tuple that I missed? Also, I note that none of your examples need TypeTuples at all, so it would appear that your use case do not necessitate the unification of TypeTuple with Tuple. Though that would be nice conceptually, it does introduce a lot of complications into the language and require addressing some non-trivial issues which, taking a step back, perhaps we don't *need* to address, because they are of little or no practical use? T -- Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce
Re: A Discussion of Tuple Syntax
On Wednesday, 21 August 2013 at 00:38:31 UTC, Andrei Alexandrescu wrote: On 8/20/13 5:28 PM, Tyler Jameson Little wrote: Instead there have been 1001 proposals for new syntax for tuple literals, one cuter than the next. I agree. However, that syntax issue is the bikeshed and it keeps everyone's mind busy. So, I propose to pick-up a syntax, even a provisional one, let's say that &(a,b) that I kinda like, then stick with it. This will free the way for more important and fundamental issues and, on the way, it will also allow to discover the eventual shortcomings of the syntax that was picked-up.
Re: A Discussion of Tuple Syntax
On Wednesday, 21 August 2013 at 00:38:31 UTC, Andrei Alexandrescu wrote: 1. What do we need? Nothing. Everything discussed thus far can already be done in D, but with either a little or a lot of tedium and error-prone hackery. What I think would benefit D is a cleanup/simplification/sugarization of the current tuple situation. TypeTuple causes no end of grief and confusion. 2. What does a solution look like in the current language? See Kenji's DIP for Bearophile's example of tuple use in the current language. Throughout this discussion there have also been several other examples of how TypeTuple misbehaves. 3. Which parts of the solution are frequent/cumbersome enough to warrant a language change? IMO, TypeTuple auto-expansion and mixing of data/types within the same TypeTuple. It's also annoying that TypeTuples are not first-class values, and it would be a pity if we did not come up with some sort of tuple literal syntax, but that situation can be worked around by wrapping it in Tuple. Instead there have been 1001 proposals for new syntax for tuple literals, one cuter than the next. Wouldn't you call this characterization a little unfair, when there were numerous posts also discussing relevant issues and semantics? I'd say the ratio of form:function has been pretty good thus far.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 18:51:23 UTC, Andrei Alexandrescu wrote: On 8/19/13 11:39 PM, Timon Gehr wrote: On 08/20/2013 02:18 AM, Andrei Alexandrescu wrote: Why would it be necessary to return an object of type TypeTuple (i.e. template tuple)? - Elegance. Eg: auto seq(T...)(T arg){ return arg; } auto fold(alias a,S,R)(S start, R range){ ... } seq(0,[1,2,3]).fold!((a,b)=>a+b); But this is again a value tuple not a template tuple, no? Andrei A value tuple IS a tuple. What we call a tuple here as nothing to do with a tuple.
Re: A Discussion of Tuple Syntax
On Tuesday, August 20, 2013 23:25:03 Michel Fortin wrote: > On 2013-08-21 00:38:30 +, Andrei Alexandrescu > > said: > > 1. What do we need? > > I think D needs less tuples. It has two kinds (language kind and > Phobos's kind), which is confusing. It should have just one that covers > most use cases. I honestly think that trying to combine Tuple and TypeTuple would increase the confusion, because you'd be forcing the compiler to decide what you meant in any given situation, and from the programmer's perspective, it will frequently be non-obvious whether what's being done is being done at compile time or at runtime. The main problem I see is that TypeTuple is badly named. Heck, the fact that it's even referred to as a tuple is problematic, since it isn't really, as it always expands. And the fact that it's the "built-in" tuple but requires a library solution to be able to actually declare it is a bit odd. So, I think that creating as syntax for the built-in tuples in order to get rid of TypeTuple would clean things up. But I see no reason to try and combine any of that with Tuple, as Tuple and TypeTuple do fundamentally different things. The main gain I see is simply in cleaning up the naming mess, and by making it so that the built-in tuple actually has a built-in syntax, the language is cleaner and should be easier to understand (especially if we can come up with a name other than tuple or built-in tuple to call them so that they stop getting confused with Tuple). - Jonathan M Davis
Re: A Discussion of Tuple Syntax
On 2013-08-21 00:38:30 +, Andrei Alexandrescu said: 1. What do we need? I think D needs less tuples. It has two kinds (language kind and Phobos's kind), which is confusing. It should have just one that covers most use cases. So in essence a tuple should: 1. be able to contain anything you can put as a template argument 2. be able to be packed and expanded as needed And the above should be usable in these situations: - using a type tuple as a variable's type - unpacking a tuple as function/template arguments - using a tuple as a return type - assigning a value tuple to an alias-of-variables tuples - using foreach with a tuple - pass a tuple by reference to a function - return a tuple by reference from a function - slice a tuple - concat tuples I'm on the fence about taking the address of a tuple. Not being able to take the address has an upside: if the function call ABI pass each component of a tuple as one ref parameter under the hood, then you could pass tuple of aliases as function arguments. For instance, here I'd be swapping variables a, c, and e with variables b, d, and f with just one call to swap: int a, b, c, d, e, f; swap(...(a, c, e), ...(b, d, f)); (Note: using "..." syntax to create packed tuple literals. See my other post about how that'd work.) -- Michel Fortin michel.for...@michelf.ca http://michelf.ca
Re: A Discussion of Tuple Syntax
On 2013-08-20 20:16:48 +, Andrei Alexandrescu said: On 8/20/13 6:57 AM, Dicebot wrote: Preserve auto-expansion. That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.) I used to like auto expansion, probably because it was convenient for what I was doing. I always feared that not having auto-expansion would make my code ugly. But now that I've been working with C++11 with its variadic template parameters that don't auto expand, I'm quite fond of it. To expand a "tuple" all you have to do is suffix it with "...", example: template < typename A, typename B, typename... C > shared_ptr< Expression > make_exp(Operator op, A first, B second, C... more) { return make_exp(op, make_exp(op, first, second), more...); } Now, if we could get rid of auto expansion while requiring a "..." suffix to expand a tuple, I think it'd be really great. Can't we steal this from C++? What follows is how I'd transpose this to D. First, let's put the three-dots *before* the template argument (somewhat as in C++) to get a packed tuple. This will ensure backward compatibility (if you put the three-dots after, like current D, you'd still get an auto-expanding tuple): Expression make_exp(A, B, ...C)(Operator op, A first, B second, C... more) { return make_exp(op, make_exp(op, first, second), more...); } Basically, the rule is simple: three dots before an expression means "pack", three dots after it means "expand". In the example above, the template argument C is a packed tuple type. Expanding C in the function's argument list means that we're expecting arguments to come as separate argument and not as one packed tuple argument. Those separate arguments get packed into the "more" parameter variable, which is of type C, which is then expanded as requested by the three-dot suffix when calling the function. Extrapolating things a little, a packed tuple type could be expressed like this: ...(int, double) x; // a packed type tuple variable x = ...(1, 2.3); // create a packed value tuple and assign it to x. writeln(x...); // expand tuple to make it two separate arguments writeln(x); // keep it packed and you're passing it as one argument ...(int, ...(double, float)) y; // nested tuples Also, you can take an auto-expanding tuple and make it packed: template (X...) { ...(X) x; // auto-expanding tuple becomes packed } Which is the same as making it packed directly from the argument list: template (...X) { X x; // already packed tuple } And since this is a "language-type" tuple, it can contain a mix of expression, types, aliases, etc, and therefore can be used like this: ...(int, "ha!", writeln); // can contain anything usable as a template argument Other examples: int x; double y; ...(x, y) = ...(1, 2.3); // multiple assignments ...(x, y) = getPackedTuple(); And finally, we could support expanding whole expressions that have a packed tuple in them by shifting the expanding three-dot outward, as you can do with C++11: ...(int, double) x; func(exp(x+5)...); // expands to: func(exp(x[0]+5), exp(x[1]+5)); Wouldn't that be great? After some time, if desired, auto-expanding tuples could be deprecated. The migration path wouldn't be too hard, nor too verbose: just add those three-dots right and left as needed. -- Michel Fortin michel.for...@michelf.ca http://michelf.ca
Re: A Discussion of Tuple Syntax
On Wednesday, 21 August 2013 at 01:50:49 UTC, Dicebot wrote: On Wednesday, 21 August 2013 at 00:38:31 UTC, Andrei Alexandrescu wrote: I think this whole thread is approaching things wrongly. It should be: 1. What do we need? Also my answer to this question may probably be not very mainstream according to this thread :) I thing that most of all we need definition-driven tuple classification as opposed to current behavior-driven one (here is the built-in tuple and look what shiny stuff it can do). Everything else is just a consequence.
Re: A Discussion of Tuple Syntax
On Wednesday, 21 August 2013 at 00:38:31 UTC, Andrei Alexandrescu wrote: I think this whole thread is approaching things wrongly. It should be: 1. What do we need? 2. What does a solution look like in the current language? 3. Which parts of the solution are frequent/cumbersome enough to warrant a language change? Exactly. Though if we want to avoid breaking changes as much as possible, maybe it should be more like this: 2. What use cases current language state cover? 3. How it can me remodeled to have less confusing semantics / definitions while covering as much old behavior as possible? I am struggling at (3) because current applications love to contradict each other when any relatively simple rule set is imagined. Basically there are 3 distinct (according to allowed usage) cases for built-in tuples: 1) pure type-tuple 2) mixed compile-time tuple (literals and other compile-time expressions go here) 3) run-time tuple over set of variables/parameters As far as I can see, covering std.typecons.Tuple in similar way is trivial - but re-defining those 3 in a meaningful way is really challenging. Maybe you have any opinion as a concept author? :P
Re: A Discussion of Tuple Syntax
Andrei Alexandrescu: 1. What do we need? We can live fine without a syntax to manage tuples, so strictly speaking we need nothing (almost, I'd like to kill one currently accepted syntax, see below). But when you write D in a high-level style, and you are used to other functional or scripting languages, in several cases you desire a more handy/compact syntax to manage tuples. Tuples are simple data structure, so the operations you want to do with them are few and simple: - To define a tuple of various fields of different type, that is a tuple literal, to create a tuple; - A way to read and write items of a tuple (unless it's read-only), D uses a nice syntax [i] as arrays indexing, but i can't be a run-time value. - With the Phobos tuple we are used to giving names to fields. It's handy, but it's not so necessary if you have a way to assign tuple items to variables, defined in-place. There are several places where pulling apart a tuple in that way is useful, here I use a basic syntax to be more clear: Assignments: auto (a, b) = t1; In function signatures: auto mult = ((int x, int y)) => x * y; In foreach loops: void main() { import std.range, std.stdio; auto a = [10, 20]; auto b = [100, 200]; // Currently D supports this syntax: foreach (x, y; zip(a, b)) writeln(x, " ", y); // But it's unreliable, now x is the index of the // array instead of the first tuple fields, so I // think this feature of D should be killed as // soon as possible: foreach (x, y; zip(a, b).array) writeln(x, " ", y); } Its output: 10 100 20 200 0 Tuple!(int, int)(10, 100) 1 Tuple!(int, int)(20, 200) In (final) switch cases, this also shows one use case and one usefulness of wildcards (and the usage of an unapply() standard method, as explained a bit here: http://d.puremagic.com/issues/show_bug.cgi?id=596 ): final switch (tup1) { case (0, 10): break; case (0, ?): break; case (?, ?): break; } An nice example usage of such basic pattern matching is visible here, to implement the insertion in a red-black-tree. This is a well known hairy operation to do in languages as C: http://rosettacode.org/wiki/Pattern_matching This is how insertion and balancing is done in Haskell (this needs long lines and it probably comes out unreadable here, so see here for a better visualization: http://rosettacode.org/wiki/Pattern_matching#Haskell ): data Color = R | B data Tree a = E | T Color (Tree a) a (Tree a) balance :: Color -> Tree a -> a -> Tree a -> Tree a balance B (T R (T R a x b) y c ) z d = T R (T B a x b) y (T B c z d) balance B (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d) balance B a x (T R (T R b y c) z d ) = T R (T B a x b) y (T B c z d) balance B a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d) balance col a x b = T col a x b insert :: Ord a => a -> Tree a -> Tree a insert x s = T B a y b where ins E = T R E x E ins s@(T col a y b) | x < y = balance col (ins a) y b | x > y = balance col a y (ins b) | otherwise = s T _ a y b = ins s There are few more little pieces that are useful, like unpacking an array: string s = "red blue"; auto (col1, col2) = s.split; Hara has suggested pattern matching in if statements, that is a special case of the pattern matching in switch cases, and it's sometimes useful (similar code is common in Scala): if (auto {1, y} = tup) {...} That is equivalent to: if (tup[0] == 1) { auto y = tup[1]; ... } It looks simple and not so essential, but when tuples become a little more more complex pattern matching shows it usefulness better, as in the Haskell example. Languages as Haskell and Rust also show that even a simple version of pattern matching is handy when you manage types like Nullable() (that need to define an unapply() standard method). (It's not unthinkable to support pattern matching in function signatures too, as often used in many functional languages as OCaML and Haskell, but this is an orthogonal feature, it could be added later, and we can probably live without it for now). Others have suggested a syntax with ... to gather more than one item, or to ignore more than one item with ?... Such syntax is handy with arrays but with tuples, that are often short and of well known size I think it's not so much useful, so it can be left for later. 2. What does a solution look like in the current language? The DIP32 shows a complete very small program that I wrote for RosettaCode site, it computes the Huffman encoding of a given string: http://wiki.dlang.org/DIP32 import std.stdio, std.algorithm, std.typecons, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(s => tuple(s[1], [tuple(s[0], "")
Re: A Discussion of Tuple Syntax
2013/8/21 Andrei Alexandrescu > I think this whole thread is approaching things wrongly. It should be: > > 1. What do we need? > Both a. Built-in tuple construction syntax and b. Built-in tuple deconstruction syntax 2. What does a solution look like in the current language? > a. alias Types = {int, long}; auto values = {1, "str"}; b. auto {a, b} = tup; // on variable declaration {a, b} = tup; // on assignment expression ... 3. Which parts of the solution are frequent/cumbersome enough to warrant a > language change? > a1. To make std.typetuple.TypeTuple template unnecessary. a2. To keep std.typecons.Tuple as-is. a3. To make expression tuple more usable. Today making the built-in tuple of runtime values needs unavoidable runtime cost. int x, y; auto tup = TypeTuple!(x+1, y+2); // NG, template cannot take x+1 and y+2 because they are // expressions which cannot be evaluated in compile time auto tup = tuple(x+1, y+2); // NG, tup is not a builti-in tuple (it is an object of std.typecons.Tuple struct). auto tup = tuple(x+1, y+2).expand; // OK, tup is a builti-in tuple. But we cannot avoid the cost to pack in std.typecons.Tuple. Built-in tuple syntax would solve the issue. auto tup = {x+1, y+2}; // tup is a built-in tuple, and it would be equivalent with: // auto tup[0] = x+1, tup[1] = x*2; // pseudo code // so no packing cost in runtime is there. b. To make deconstruction code more readable. auto tup = std.typecons.tuple(1, "str"); auto a = tup[0], b = tup[1];// Currrent: // Deconstruct two fields in tup to the two variables a and b auto {a, b} = tup;// New syntax: // Deconstruct syntax would recognize alias this tuple, // and expand it automatically. Kenji Hara
Re: A Discussion of Tuple Syntax
2013/8/21 Timon Gehr > On 08/20/2013 10:16 PM, Andrei Alexandrescu wrote: > >> (There have been a few discussions in this group; I've come to think >> auto expansion is fail.) >> > > +1. :) > +1, too. Since I wrote a compiler patch for the auto expansion on function arguments, but it was rejected by Andrei. http://d.puremagic.com/issues/show_bug.cgi?id=2779 And today, I'm mostly consent in his opinion. Kenji Hara
Re: A Discussion of Tuple Syntax
On 8/20/13 5:28 PM, Tyler Jameson Little wrote: OT: is the only thing stopping us from using the nice (x,y) syntax for tuples the comma operator? If so, can we take that mis-feature behind the woodshed and shoot it? I sincerely hope nobody is relying on it... I think this whole thread is approaching things wrongly. It should be: 1. What do we need? 2. What does a solution look like in the current language? 3. Which parts of the solution are frequent/cumbersome enough to warrant a language change? Instead there have been 1001 proposals for new syntax for tuple literals, one cuter than the next. Andrei
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 21:25:11 UTC, Andrei Alexandrescu wrote: On 8/20/13 1:24 PM, Dicebot wrote: That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.) :O it is awesome! "Spawn of Satan" would be a tad more appropriate. +1 I can stand explicit expansion though, like Go's variadic argument ellipses syntax: http://golang.org/doc/effective_go.html#append OT: I'm not really a fan of D's variadic function syntax. I'd prefer it to be explicit: int sum(int[] ar ...) { } int[3] foo = [4, 5, 6]; sum(foo...); sum(3, 4, foo...); // on my wish list... Stuff like foo(myStructInstance.tupleof) is very powerful tool for generic interfaces. One problem with automatic expansion is that now there's a whole new kind - a single expression that doesn't quite have one value and one type, but instead is an explosion of other expressions. snip... Alternatively, we could do what Go does and prevent all packing altogether (if I understand what Go does correctly). That is, if a function returns a tuple you can't even keep that tuple together unless you use some handcrafted solution. In that case, before long there will be some "Pack" structure and some pack helper function, viz. the converse of .expand. +1 I've been itching for multiple returns in D for a long time, and this seems like a nice way to add it in. I think I'd prefer to use tuple syntax instead though, just so there's less magic: (int, int) doStuff() { return (1, 2); } // syntax error auto a, b = doStuff(); // ok auto (a, b) = doStuff(); Clearly both packing and unpacking tuples are necessary. snip... Andrei OT: is the only thing stopping us from using the nice (x,y) syntax for tuples the comma operator? If so, can we take that mis-feature behind the woodshed and shoot it? I sincerely hope nobody is relying on it...
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 21:25:11 UTC, Andrei Alexandrescu wrote: Ionno so I just summarized it. Andrei I guess I need just deal with it and accept as given then :) Okay, it does not really change much for main issue I wanted to pay attention to - thin edge between run-time expression tuples and compile-time ones. Only problematic moment I can see so far is that compile-time tuples can contain both types and expressions at the same time. But this can be solved by considering them non-instantiatable type tuples - similar to existing handling. Or just call them "template argument lists" to be 100% straight and reserve word "tuple" for expression tuple while preserving "typeof" relation (and enhancing expression tuples with ABI). Sounds like dreaming? :)
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 21:25:11 UTC, Andrei Alexandrescu wrote: "Spawn of Satan" would be a tad more appropriate. I can not agree more.
Re: A Discussion of Tuple Syntax
On Tue, Aug 20, 2013 at 05:12:43AM +0200, Meta wrote: > Aggh, misposted. Let's try that again. > > On Tuesday, 20 August 2013 at 02:51:20 UTC, Meta wrote: > >On Tuesday, 20 August 2013 at 01:06:28 UTC, H. S. Teoh wrote: > >>Actually, reading through DIP32 again, it sounds like Kenji is > >>proposing the *same* syntax for both built-in tuples and > >>std.typecons.Tuple. In the code example under "Generic > >>type/expression tuple syntax", he refers to them respectively as > >>"tuple type" and "tuple value". Mixing is also allowed (e.g., in the > >>"alias Fields" line). > > Maybe mixing should be disallowed, then, unless your tuple was > constructed from a variadic template argument list. [...] I don't like this "unless". Either we support mixing, or we don't. Adding in "unless" adds unnecessary complication, which will inevitably have a ripple effect that adds complications everywhere. Next thing you know, Phobos will acquire a template that returns a tuple of its arguments -- as a workaround for the inability to manually construct a mixed tuple, and then we'll have a rehash of this thread, this time surrounding how to merge/get rid of std.typecons.createMixedTuple. I think any workable design of tuples must include a sane way of working with mixed tuples, because they can and do appear in template arguments. T -- Без труда не выловишь и рыбку из пруда.
Re: A Discussion of Tuple Syntax
On 08/20/2013 10:16 PM, Andrei Alexandrescu wrote: (There have been a few discussions in this group; I've come to think auto expansion is fail.) +1. :)
Re: A Discussion of Tuple Syntax
On 8/20/13 1:24 PM, Dicebot wrote: That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.) :O it is awesome! "Spawn of Satan" would be a tad more appropriate. Stuff like foo(myStructInstance.tupleof) is very powerful tool for generic interfaces. One problem with automatic expansion is that now there's a whole new kind - a single expression that doesn't quite have one value and one type, but instead is an explosion of other expressions. The problem now is how to contain these things - presumably one should associate a self-exploding tuple with a symbol: auto pack = functionReturningTuple(); The symbol should not expand when, for example, assigned to another: auto packCopy = pack; However, the symbol _should_ expand "where it should", presumably in a function code: auto m = min(pack, 0); So this will invoke min with various numbers of arguments depending on what pack contains. Same with e.g. writeln(pack); Before one even realizes it, the matter of when to expand vs. when not to expand becomes a thing. That thing will have people confused (because now an expression isn't what it seems; it may be, in fact, several expressions at once). It will get explained in articles and books. There will be debates about choices of automatic unpacking vs. not in which reasonable people may disagree. People will get all confused about it. Generic code won't be able to tell much about what's going on because arity can't be guaranteed anymore. Alternatively, we could do what Go does and prevent all packing altogether (if I understand what Go does correctly). That is, if a function returns a tuple you can't even keep that tuple together unless you use some handcrafted solution. In that case, before long there will be some "Pack" structure and some pack helper function, viz. the converse of .expand. Clearly both packing and unpacking tuples are necessary. The question is what the default is. My argument here is that keeping one expression/symbol have one value is so deeply embedded in the semantics (and the ethos for that matter) of D, that it would be a major step backwards to retrofit it all to accommodate self-expanding tuples. Not speaking for other languages, I think it's great that in D writeln(x); is known to take an argument, whereas writeln(x.expand); takes zero or more arguments. It is the best compromise I can imagine that keeps the language sealed tight. Do you remember any keywords to look for to find those discussions? Ones I remember were mostly about auto-expansion _not_ happening in some reasonable places. Ionno so I just summarized it. Andrei
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 20:16:54 UTC, Andrei Alexandrescu wrote: On 8/20/13 6:57 AM, Dicebot wrote: I am proposing something more radical. Deprecate _both_ TypeTuple and Tuple. Clearly define the difference between built-in type tuple and expression tuple (latter being instance of former). But that can't be the case. Can you elaborate on this? I can only judge by observable behavior which clearly says that typeof(TypeTuple!(2, 3)) is TypeTuple!(int, int). And when you use variadic template argument syntax in functions, you use "T..." (which is a built-in type tuple) to declare arguments (which is built-in expression tuple). There are probably some inconsistencies in implementation but this is the observable behavior that can be declared official. Preserve auto-expansion. That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.) :O it is awesome! Stuff like foo(myStructInstance.tupleof) is very powerful tool for generic interfaces. Do you remember any keywords to look for to find those discussions? Ones I remember were mostly about auto-expansion _not_ happening in some reasonable places.
Re: A Discussion of Tuple Syntax
On 8/20/13 8:43 AM, Kenji Hara wrote: 2013/8/20 Dicebot mailto:pub...@dicebot.lv>> What we have here are two completely different _built-in_ tuple types. "T" is pure template arguments list and is pretty much equal TypeTuple!(int, int). But what is "args"? It uses the very same built-in tuple syntax but it is much closer to std.typecons.Tuple in its behavior (actually, latter is implemented in terms of it as I was pointed out) - it is an entity that provides abstraction of top of group of run-time values. It does not have any binary structure on its own (built on top of existing values) but observable semantic are very "run-time'ish". "args" is a built-in tuple of two function parameter variables. In binary level, args[0] and args[1] could be bounded to completely individual storage. (Even if args[1] is on stack, args[0] may be on register) On the other hand, std.typecons.Tuple is a struct. Its second field has the memory address which follows of the first field. Ome question would be whether appropriate inlining could solve the performance disadvantage of Tuple. Andrei
Re: A Discussion of Tuple Syntax
On 8/20/13 6:57 AM, Dicebot wrote: I am proposing something more radical. Deprecate _both_ TypeTuple and Tuple. Clearly define the difference between built-in type tuple and expression tuple (latter being instance of former). But that can't be the case. Preserve auto-expansion. That would be problematic to say the least. (There have been a few discussions in this group; I've come to think auto expansion is fail.) Provide two different literals to remove ambiguity between referencing symbol as a value and referencing it as an alias. Make compiler auto-generate Tuple-like struct type for expression tuples that need to be returned from functions. Create some new type in std.typecons for those who don't want auto-expansion. It won't even break stuff. That sounds good :o). Andrei
Re: A Discussion of Tuple Syntax
On Tue, Aug 20, 2013 at 04:10:29PM -0400, Jonathan M Davis wrote: > On Tuesday, August 20, 2013 13:06:27 H. S. Teoh wrote: > > On Tue, Aug 20, 2013 at 11:53:50AM -0700, Andrei Alexandrescu wrote: > > > Somebody shoot me. > > > > [...] > > > > BANG! > > Don't kill him. He hasn't finished the allocators yet! ;) Well, as my signature line said: They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill ;-) T -- Why can't you just be a nonconformist like everyone else? -- YHL
Re: A Discussion of Tuple Syntax
On Tuesday, August 20, 2013 13:06:27 H. S. Teoh wrote: > On Tue, Aug 20, 2013 at 11:53:50AM -0700, Andrei Alexandrescu wrote: > > Somebody shoot me. > > [...] > > BANG! Don't kill him. He hasn't finished the allocators yet! ;) - Jonathan M Davis
Re: A Discussion of Tuple Syntax
On Tue, Aug 20, 2013 at 11:53:50AM -0700, Andrei Alexandrescu wrote: > On 8/20/13 3:38 AM, eles wrote: > >On Friday, 16 August 2013 at 21:07:52 UTC, Meta wrote: > >>A good, comprehensive design has the potential to make tuples easy > >>to use and understand, and hopefully clear up the unpleasant > >>situation we have currently. A summary of what has been discussed so > >>far: > >> > >>- (a, b) is the prettiest syntax, and it also completely infeasible > >> > >>- {a, b} is not as pretty, but it's not that bad of an alternative > >>(though it may still have issues as well) > >> > >>- #(a, b) is unambiguous and would probably be the easiest option. I > >>don't think it looks too bad, but some people might find it ugly and > >>noisy > > > >What about: > > > >!!(a, b) > > > >? Yes, is long, but is type-able quite fast. > > > >Alternative would be: > > > >??(a, b) > > > >. > > Somebody shoot me. [...] BANG! T -- They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
Re: A Discussion of Tuple Syntax
On 08/20/2013 08:51 PM, Andrei Alexandrescu wrote: On 8/19/13 11:39 PM, Timon Gehr wrote: On 08/20/2013 02:18 AM, Andrei Alexandrescu wrote: Why would it be necessary to return an object of type TypeTuple (i.e. template tuple)? - Elegance. Eg: auto seq(T...)(T arg){ return arg; } auto fold(alias a,S,R)(S start, R range){ ... } seq(0,[1,2,3]).fold!((a,b)=>a+b); But this is again a value tuple not a template tuple, no? Andrei In my understanding the former is a special case of the latter. Since you invented those terms, I might be mistaken. How do you distinguish between them?
Re: A Discussion of Tuple Syntax
On 08/20/2013 05:18 PM, bearophile wrote: s = "red blue" (a, b) = s.split() a 'red' b 'blue' It's supported in Haskell too: Prelude> let s = "red blue" Prelude> let [a, b] = words s Prelude> a "red" Prelude> b "blue" Bye, bearophile Any language with algebraic data types supports this.
Re: A Discussion of Tuple Syntax
On 08/20/2013 09:57 PM, Timon Gehr wrote: On 08/20/2013 05:18 PM, bearophile wrote: s = "red blue" (a, b) = s.split() a 'red' b 'blue' It's supported in Haskell too: Prelude> let s = "red blue" Prelude> let [a, b] = words s Prelude> a "red" Prelude> b "blue" Bye, bearophile Any language with algebraic data types supports this. (Some will only allow it if you can prove the match is total within them, of course.)
Re: A Discussion of Tuple Syntax
On 8/20/13 3:38 AM, eles wrote: On Friday, 16 August 2013 at 21:07:52 UTC, Meta wrote: A good, comprehensive design has the potential to make tuples easy to use and understand, and hopefully clear up the unpleasant situation we have currently. A summary of what has been discussed so far: - (a, b) is the prettiest syntax, and it also completely infeasible - {a, b} is not as pretty, but it's not that bad of an alternative (though it may still have issues as well) - #(a, b) is unambiguous and would probably be the easiest option. I don't think it looks too bad, but some people might find it ugly and noisy What about: !!(a, b) ? Yes, is long, but is type-able quite fast. Alternative would be: ??(a, b) . Somebody shoot me. Andrei
Re: A Discussion of Tuple Syntax
On 8/19/13 11:39 PM, Timon Gehr wrote: On 08/20/2013 02:18 AM, Andrei Alexandrescu wrote: Why would it be necessary to return an object of type TypeTuple (i.e. template tuple)? - Elegance. Eg: auto seq(T...)(T arg){ return arg; } auto fold(alias a,S,R)(S start, R range){ ... } seq(0,[1,2,3]).fold!((a,b)=>a+b); But this is again a value tuple not a template tuple, no? Andrei
Re: A Discussion of Tuple Syntax
Kenji Hara: My position to such a feature is constant. Even if your opinion is not changed, I have to show that common tuple-related features to the other persons that are reading this thread, because it's an an idea to keep in account (even if it's refused), and some other person could have an opinion different from yours. However, array unpacking would require *hidden* runtime boundary check. In above example code, assert(tup[2].length == 2); // or if (tup[2].length != 2) throw Error("Runtime mismatch of unpacking pattern"); // or similar should be implicitly inserted by compiler, even in @system code. I'm not sure that is acceptable cost or not. Runtime boundary checks and tests are not needed if you unpack a fixed-sized array: auto tup = Tuple!(int, string[2])(1, ["red", "blue"]); auto {x, [c1, c2]} = tup; Regarding the de-structuring of dynamic arrays, I think it's not too much different than this: void main() { int[] a = [10, 20, 30]; int[2] b = a; } If you compile and run it without switches, you get at run-time: object.Error: lengths don't match for array copy, 2 = 3 While I receive no run-time errors if I compile with -noboundscheck. Bye, bearophile
Re: A Discussion of Tuple Syntax
Dicebot: bearophile I really respect informational contribution you have been doing in D community but in my opinion mentioning random snippets from other languages is the least useful thing possible in this thread. It is even less useful than discussing syntax. Look better at them, those aren't random snippets, they show one more case of de-structuring, creating a tuple of variables out of an array. It's a commonly used operation in two very well designed languages that have tuples. Here we are discussing tuples and their packing and unpacking, so the two examples I've shown are very relevant, even if we decide to not support that specific feature. Bye, bearophile
Re: A Discussion of Tuple Syntax
2013/8/20 bearophile > So, std.typecons.Tuple _is not special_. You can define another Tuple >> struct in the same way. >> We should not define new syntax for the library utility >> std.typecons.Tuple. >> > > With your idea is there a way to unpack a short array into variables? This > is an handy operation I do often in Python: > > s = "red blue" (a, b) = s.split() a >>> 'red' > >> b >>> 'blue' > My position to such a feature is constant. We should not conflate tuple unpacking and array unpacking. But, extending unpacking syntax mentioned in DIP32 might help it. // Just an idea: extending DIP32 unpacking syntax import std.typecons : tuple; auto tup = tuple(1, tuple(2,3), [4,5]); { auto a, {b, c}, [d, e] } = tup; //{ auto a, [b, c], {d, e} } = tup; // cause compile-time error assert(a == 1); assert(b == 2 && c == 3); assert(d == 4 && e == 5); However, array unpacking would require *hidden* runtime boundary check. In above example code, assert(tup[2].length == 2); // or if (tup[2].length != 2) throw Error("Runtime mismatch of unpacking pattern"); // or similar should be implicitly inserted by compiler, even in @system code. I'm not sure that is acceptable cost or not. Kenji Hara
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 13:14:46 UTC, Timon Gehr wrote: On 08/20/2013 12:38 PM, eles wrote: What about: !!(a, b) This already has a meaning. True :( I dunno why I took it for some kind of binary operator instead of a unary one. I was too quick on my keyboard.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 15:43:39 UTC, Kenji Hara wrote: On the other hand, std.typecons.Tuple is a struct. Its second field has the memory address which follows of the first field. Yes but as you have said exact storage of built-in tuple elements is not defined - it can be distinct elements or struct fields, compiler is free to chose any. I am not asking about internal implementation but about behavior observable by user in typical cases - it is most important topic here. Automatically packing/unpacking would need hidden runtime cost. Will it be any more costly then returning std.typecons.Tuple and unpacking it manually? And, well, do we have _any_ other option of returning a tuple with no runtime costs right now anyway? Considering we are far away from stable ABI that does not seem to be a real issue.
Re: A Discussion of Tuple Syntax
2013/8/20 Dicebot > What we have here are two completely different _built-in_ tuple types. "T" > is pure template arguments list and is pretty much equal TypeTuple!(int, > int). But what is "args"? It uses the very same built-in tuple syntax but > it is much closer to std.typecons.Tuple in its behavior (actually, latter > is implemented in terms of it as I was pointed out) - it is an entity that > provides abstraction of top of group of run-time values. It does not have > any binary structure on its own (built on top of existing values) but > observable semantic are very "run-time'ish". > "args" is a built-in tuple of two function parameter variables. In binary level, args[0] and args[1] could be bounded to completely individual storage. (Even if args[1] is on stack, args[0] may be on register) On the other hand, std.typecons.Tuple is a struct. Its second field has the memory address which follows of the first field. What bothers me is the question "can we clean this up and bring those two > worlds together, even at cost of minor breakage?". I am not speaking about > literals here or unpacking or whatever. I am speaking about re-defining > semantics so that struct-based Tuple and built-in syntax sugar in form of > run-time expression tuple both get merged into one built-in construct which > can be used in wider variety of places. > At least right now I can't find any technical objections why expression > tuple can't use std.typecons.Tuple ABI when returned from function but > still be automatically expanded when passed into functions. Kenji, what is > your your opinion about complexity to implement something like that in DMD? > Automatically packing/unpacking would need hidden runtime cost. Kenji Hara
Re: A Discussion of Tuple Syntax
bearophile I really respect informational contribution you have been doing in D community but in my opinion mentioning random snippets from other languages is the least useful thing possible in this thread. It is even less useful than discussing syntax.
Re: A Discussion of Tuple Syntax
s = "red blue" (a, b) = s.split() a 'red' b 'blue' It's supported in Haskell too: Prelude> let s = "red blue" Prelude> let [a, b] = words s Prelude> a "red" Prelude> b "blue" Bye, bearophile
Re: A Discussion of Tuple Syntax
Kenji Hara: So, std.typecons.Tuple _is not special_. You can define another Tuple struct in the same way. We should not define new syntax for the library utility std.typecons.Tuple. With your idea is there a way to unpack a short array into variables? This is an handy operation I do often in Python: s = "red blue" (a, b) = s.split() a 'red' b 'blue' Bye, bearophile
Re: A Discussion of Tuple Syntax
On 20 August 2013 16:39, Timon Gehr wrote: > On 08/20/2013 02:18 AM, Andrei Alexandrescu wrote: > >> >> Why would it be necessary to return an object of type TypeTuple (i.e. >> template tuple)? >> > > - ABI > > Multiple return values could use a more efficient ABI than struct > instances because they do not have an address. > *** this I've been banging this drum for years! However this discussion resolves, I just hope it allows for convenient and efficient MRV's. Obviously it should be syntactically convenient, and assignment of MRV to callee locals should be convenient too. But more importantly, I'd like to see ALL the argument registers re-used to return multiple values, rather than just the first one. They're just sitting there begging to be used, and in many cases, would lead to some great efficiency improvements across function calls/returns. Especially on non-x86 architectures. Perhaps one of the most common causes for (otherwise unnecessary) inlining of functions is because of the terrible legacy ABI for returning multiple values from functions.
Re: A Discussion of Tuple Syntax
On Mon, Aug 19, 2013 at 09:41:24PM -0700, Jonathan M Davis wrote: > On Monday, August 19, 2013 13:45:33 Andrei Alexandrescu wrote: > > but once the bikeshed is up for painting, the > > rainbow won't suffice. > > LOL! I'm going to have to remember that one. [...] That's a classic. I'm st^H^H borrowing that for my random signatures file. :) T -- There's light at the end of the tunnel. It's the oncoming train.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 13:57:49 UTC, Dicebot wrote: Syntax sugar will only hide and eventually increase confusion, it won't solve the semantical issue. This is why we are having this topic IMHO, bunch of quite experienced D developers can't even agree on how existing feature behaves :) Including those who is it all the time! It is clear indicator that syntax is not the one to blame. My suggestions are predicated on the fact that I think tuples are more or less fine as-is. They just need some syntactic sugar for literals and destructuring/pattern matching to make them more usable. Built-in tuples are confusing, I think, because they're named TypeTuple, which makes people conflate them with std.typecons.Tuple. Another reason is that TypeTuple can contain both values and types at the same time, so it's confusing as to what you can do with them. "Why can you only assign a TypeTuple to a variable if it contains values but not types? Why, then, can't you return TypeTuple!(1, 2.3) from a function? Is it not really a variable? Why does `alias t = TypeTuple!(1, 2.3)` work, but `alias n = 3` not work? Is this f*$#ing thing a value or a type?" - D Newbie Everything else aside, I think the best possible change that would make the whole tuple situation better is to limit or outright ban the mixing of values and types within TypeTuple. Even better is to always treat TypeTuple as a type, and not a value. That ship has already sailed, but we can at least try to enforce it through convention. I am proposing something more radical. Deprecate _both_ TypeTuple and Tuple. Clearly define the difference between built-in type tuple and expression tuple (latter being instance of former). Preserve auto-expansion. Provide two different literals to remove ambiguity between referencing symbol as a value and referencing it as an alias. Make compiler auto-generate Tuple-like struct type for expression tuples that need to be returned from functions. Create some new type in std.typecons for those who don't want auto-expansion. It won't even break stuff. I think my position has changed to suggest nearly the same thing. There needs to be a clear separation between built-in tuples and tuple values. A runtime tuple literal syntax helps with that. Making typeof(tuple(1, 2.3)) == TypeTuple!(int, double) helps as well, because it is now clear that one is a value and one is a type. Limiting the mixing of values and types within TypeTuple helps with that. This would allow both Tuple and TypeTuple to be deprecated. Tuple wouldn't be needed anymore, because we would have a literal syntax to replace it. It would only be there for backwards compatibility. With Tuple deprecated, std.typetuple.TypeTuple could be renamed Tuple, and we would then have only one thing with the name Tuple in the language.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 13:17:44 UTC, Meta wrote: This is the reason I had originally thought Kenji's DIP was about run-time tuples. If it's just syntactic sugar over std.typecons.Tuple (plus some extra destructuring/pattern matching stuff), it would require no ABI changes and no changes to TypeTuple semantics. The one thing it wouldn't do is unify Tuple and TypeTuple. Syntax sugar will only hide and eventually increase confusion, it won't solve the semantical issue. This is why we are having this topic IMHO, bunch of quite experienced D developers can't even agree on how existing feature behaves :) Including those who is it all the time! It is clear indicator that syntax is not the one to blame. However, with compiler support, we can make this situation better. TypeTuple stays as-is, while Tuple becomes a value instead of a type, which has its own literal syntax. This means that typeof(tuple(1, "a")) is not Tuple!(int, string), as in the struct, but TypeTuple!(int, string), as in the compiler tuple. I am proposing something more radical. Deprecate _both_ TypeTuple and Tuple. Clearly define the difference between built-in type tuple and expression tuple (latter being instance of former). Preserve auto-expansion. Provide two different literals to remove ambiguity between referencing symbol as a value and referencing it as an alias. Make compiler auto-generate Tuple-like struct type for expression tuples that need to be returned from functions. Create some new type in std.typecons for those who don't want auto-expansion. It won't even break stuff.
Re: A Discussion of Tuple Syntax
This is the reason I had originally thought Kenji's DIP was about run-time tuples. If it's just syntactic sugar over std.typecons.Tuple (plus some extra destructuring/pattern matching stuff), it would require no ABI changes and no changes to TypeTuple semantics. The one thing it wouldn't do is unify Tuple and TypeTuple. However, with compiler support, we can make this situation better. TypeTuple stays as-is, while Tuple becomes a value instead of a type, which has its own literal syntax. This means that typeof(tuple(1, "a")) is not Tuple!(int, string), as in the struct, but TypeTuple!(int, string), as in the compiler tuple. You could still do all the stuff that can be done with TypeTuple currently, but it should be strongly discouraged to mix types and values within TypeTuple, or even limited by the compiler. This creates a clear distinction between runtime tuples and TypeTuple. It becomes much easier to reason about their semantics, as we can think of TypeTuple as a type, just like int, string, double, etc., and Tuple as a value, just like 1, "a", 2.3, etc. The only difference between TypeTuple and regular types is that there are a few special static operations that can be used to manipulate it. Otherwise it behaves as a type.
Re: A Discussion of Tuple Syntax
On 08/20/2013 12:38 PM, eles wrote: What about: !!(a, b) This already has a meaning.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 09:02:29 UTC, Mr. Anonymous wrote: Why not just leave an empty space, like php does: $info = array('coffee', 'brown', 'caffeine'); // let's skip to only the third one list( , , $power) = $info; echo "I need $power!\n"; Bad idea. It's a visual thing-- quick! How wide is this tuple!?: #(,frontvar,) How long did it take you to be SURE that it's a seven-tuple? You probably aren't even equipped to measure that so don't worry about answering, but I'm guessing you start to appreciate why it's a fairly inhumane solution (how drearily typical for PHP). Ah, but what if it was supposed to be #(,front,var,)? Does that change your result, now that you're taking the sixth element rather than the seventh? All you did was forget a comma! By contrast: #(?,front,?,?,?,var,?,?) //at least gives us a better chance of parsing it correctly. My point here is we are human and we are fallible. The sooner we acknowledge and internalise that, the better equipped we are to make systems that don't suck for humans to use. In that sense, I don't even really like this format because it's still not especially resilient. Walter's talk about design that eliminates patterns of human error resonated with me quite strongly, you see. But in terms of language consistency it's not much different from array literals, so I could accept it as a compromise. -Wyatt
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 03:33:51 UTC, Kenji Hara wrote: Sorry I cannot reply to each thread comments quickly, so I discharge my opinions at once. One thing that I think does not get deserved attention is that built-in expression tuple are not limited to compile-time values. Lets consider this simple example: void foo(T...)(T args) { pragma(msg, args); pragma(msg, typeof(args)); } void main() { int a, b; foo(a, b); } --- /d147/f686.d(3): Error: variable _param_0 cannot be read at compile time /d147/f686.d(3): Error: variable _param_1 cannot be read at compile time tuple(_param_0, _param_1) (int, int) --- What we have here are two completely different _built-in_ tuple types. "T" is pure template arguments list and is pretty much equal TypeTuple!(int, int). But what is "args"? It uses the very same built-in tuple syntax but it is much closer to std.typecons.Tuple in its behavior (actually, latter is implemented in terms of it as I was pointed out) - it is an entity that provides abstraction of top of group of run-time values. It does not have any binary structure on its own (built on top of existing values) but observable semantic are very "run-time'ish". What bothers me is the question "can we clean this up and bring those two worlds together, even at cost of minor breakage?". I am not speaking about literals here or unpacking or whatever. I am speaking about re-defining semantics so that struct-based Tuple and built-in syntax sugar in form of run-time expression tuple both get merged into one built-in construct which can be used in wider variety of places. At least right now I can't find any technical objections why expression tuple can't use std.typecons.Tuple ABI when returned from function but still be automatically expanded when passed into functions. Kenji, what is your your opinion about complexity to implement something like that in DMD?
Re: A Discussion of Tuple Syntax
On 2013-08-20 05:33, Kenji Hara wrote: If you want to return multiple values from a function, you must always wrap them by std.typecons.Tuple, or other used-defined types. You cannot directly return built-in tuple from a function. (Built-in tuple return is mostly equivalent with multiple-value-return issue. However it would be mostly impossible that defining calling conversion scheme for that in portable) If I recall correctly some ABI's passes small structs in registers, at least I think it does on Mac OS X. My opinions agains various syntax proposals: #(1, "str") --> The character '#' is already used for the start of "Special Token Sequences" http://dlang.org/lex.html#Special Token Sequence It is recognized in lexing phase, so adding semantic meaning to the '#' character would be a contradict of D's principle. Quote from http://dlang.org/lex.html "The lexical analysis is independent of the syntax parsing and the semantic analysis." It depends on how it's lexed. If the compiler lexes "#line" as a single token I cannot see how it will be a problem. Then the "#" token would mean tuple literal. -- /Jacob Carlborg
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 03:33:51 UTC, Kenji Hara wrote: My opinions agains various syntax proposals: #(1, "str") --> The character '#' is already used for the start of "Special Token Sequences" http://dlang.org/lex.html#Special Token Sequence It is recognized in lexing phase, so adding semantic meaning to the '#' character would be a contradict of D's principle. Quote from http://dlang.org/lex.html "The lexical analysis is independent of the syntax parsing and the semantic analysis." Kenji Hara There are various other characters that could be substituted for # e.g. &(1, "string") http://forum.dlang.org/post/cokiixatvjmngggpb...@forum.dlang.org
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 10:38:30 UTC, eles wrote: On Friday, 16 August 2013 at 21:07:52 UTC, Meta wrote: A good, comprehensive design has the potential to make tuples easy to use and understand, and hopefully clear up the unpleasant situation we have currently. A summary of what has been discussed so far: - (a, b) is the prettiest syntax, and it also completely infeasible - {a, b} is not as pretty, but it's not that bad of an alternative (though it may still have issues as well) - #(a, b) is unambiguous and would probably be the easiest option. I don't think it looks too bad, but some people might find it ugly and noisy What about: !!(a, b) ? Yes, is long, but is type-able quite fast. Alternative would be: ??(a, b) . It's not only long, but also ugly (IMO of course).
Re: A Discussion of Tuple Syntax
On Friday, 16 August 2013 at 21:07:52 UTC, Meta wrote: A good, comprehensive design has the potential to make tuples easy to use and understand, and hopefully clear up the unpleasant situation we have currently. A summary of what has been discussed so far: - (a, b) is the prettiest syntax, and it also completely infeasible - {a, b} is not as pretty, but it's not that bad of an alternative (though it may still have issues as well) - #(a, b) is unambiguous and would probably be the easiest option. I don't think it looks too bad, but some people might find it ugly and noisy What about: !!(a, b) ? Yes, is long, but is type-able quite fast. Alternative would be: ??(a, b) .
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 18:43:37 UTC, Meta wrote: On Monday, 19 August 2013 at 16:53:06 UTC, Wyatt wrote: Can't make it a single underscore? Question mark works best then, IMO. It isn't as burdened with meanings elsewhere (sure there's ternary and possibly-match in regex, but...have I forgotten something?) It *could* be an underscore; the only thing is that the underscore is a valid variable name, so the above expression would actually be binding two variables, which might surprise someone who was expecting otherwise. I don't really care all that much, but it's something to think about. Why not just leave an empty space, like php does: $info = array('coffee', 'brown', 'caffeine'); // let's skip to only the third one list( , , $power) = $info; echo "I need $power!\n"; http://php.net/manual/en/function.list.php
Re: A Discussion of Tuple Syntax
On 08/20/2013 02:18 AM, Andrei Alexandrescu wrote: Why would it be necessary to return an object of type TypeTuple (i.e. template tuple)? - Elegance. Eg: auto seq(T...)(T arg){ return arg; } auto fold(alias a,S,R)(S start, R range){ ... } seq(0,[1,2,3]).fold!((a,b)=>a+b); (Obviously you can get close by requiring expansion at the call site.) - ABI Multiple return values could use a more efficient ABI than struct instances because they do not have an address. - Consistency A type whose instances cannot be returned from a function is just weird language design. It has no state. It may alias variables that do.
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 21:03:50 UTC, Andrei Alexandrescu wrote: I'm saying that there's a mix of useful stuff and just syntactic additions that are arguably less so. In my opinion: a) destructuring tuples in auto declarations - good to have: auto (a, b, c) = functionReturningTupleOrStaticArrayWith3Elements(); I propose the following rewrite : auto tmp = functionReturningTupleOrStaticArrayWith3Elements(); assert(tmp.length == 3); auto a = tmp[0]; auto b = tmp[1]; auto c = tmp[2]; That way any type user can take advantage of the new syntax, and it is easy to implement as it is simple rewrite rules. Note that tmp do not need to be an lvalue to avoid uneeded copies. I'd also like to see the term Tuple for variadic template parameter go away. This is confusing the hell out of everybody, as the current conversation is showing.
Re: A Discussion of Tuple Syntax
On Monday, August 19, 2013 13:45:33 Andrei Alexandrescu wrote: > but once the bikeshed is up for painting, the > rainbow won't suffice. LOL! I'm going to have to remember that one. - Jonathan M Davis
Re: A Discussion of Tuple Syntax
Sorry I cannot reply to each thread comments quickly, so I discharge my opinions at once. D's built-in tuple can contain following entities: - Type int, long, array types, AA types, user-defined types, etc - Expressions interger, string literal, etc - Symbol user-defined types, templates, template instances, etc Note that: an user-defined type would be treated as both Type and Symbol. template Test() {} alias X = std.typetuple.TypeTuple!( int, long, char[], int[int], const Object, // Types 1, "str", [1,2,3], // Expressions (literal values) object.Object, Test, Test!(), // Symbols ); If all of the elements in a built-in tuple are Type, today it is normally called "Type Tuple". If all of the elements in a built-in tuple are Expressions, today it is normally called "Expression Tuple". Note that: today we cannot create a built-in tuple without using TemplateTupleParameter. TemplateTupleParameter cannot take expressions non-literal expressions, therefore most of current built-in tuples would contains only literal values as the Expressions. std.typecons.Tuple is an artifact of built-in tuple + alias this. struct Tuple(T...) { T expand; // 'expand' is a built-in tuple of // the implicitly defined fields that // typed T[0], T[1], ... T[$-1]. // In spec, this is called "TupleDeclaration". alias expand this; // forward indexing/slicing operators to // the TupleDeclaration 'expand' } Tuple!(int, string) t; t[];// t.expand[] t[0..$];// t.expand[0..$] t[1]; // t.expand[1] So, std.typecons.Tuple _is not special_. You can define another Tuple struct in the same way. We should not define new syntax for the library utility std.typecons.Tuple. If you want to return multiple values from a function, you must always wrap them by std.typecons.Tuple, or other used-defined types. You cannot directly return built-in tuple from a function. (Built-in tuple return is mostly equivalent with multiple-value-return issue. However it would be mostly impossible that defining calling conversion scheme for that in portable) The combination of built-in tuple and alias this would be one of the case of built-in tuple of non-literal Expressions. For example, `t.expand[1]` is equivalent with the dot expression `t.__field_1` which cannot be taken by TemplateTupleParameter. Therefore, `t.expand` would be the built-in tuple of the two dot expressions `t.__field_0` and `t.__field_1`. Therefore, calling built-in tuple "Alias Tuple" would not be correct so built-in tuple can contain expressions that cannot be aliased. My opinions agains various syntax proposals: #(1, "str") --> The character '#' is already used for the start of "Special Token Sequences" http://dlang.org/lex.html#Special Token Sequence It is recognized in lexing phase, so adding semantic meaning to the '#' character would be a contradict of D's principle. Quote from http://dlang.org/lex.html "The lexical analysis is independent of the syntax parsing and the semantic analysis." Kenji Hara
Re: A Discussion of Tuple Syntax
2013/8/20 H. S. Teoh > Actually, reading through DIP32 again, it sounds like Kenji is proposing > the *same* syntax for both built-in tuples and std.typecons.Tuple. In > the code example under "Generic type/expression tuple syntax", he refers > to them respectively as "tuple type" and "tuple value". Mixing is also > allowed (e.g., in the "alias Fields" line). > Honestly, I had intended DIP32 to provide *uniform* syntax for both built-in tuple and std.typecons.Tuple. However, finally, I noticed that would be _impossible_. I'll withdraw DIP32 and will open new DIP for only built-in tuple syntax proposal. Kenji Hara
Re: A Discussion of Tuple Syntax
Aggh, misposted. Let's try that again. On Tuesday, 20 August 2013 at 02:51:20 UTC, Meta wrote: On Tuesday, 20 August 2013 at 01:06:28 UTC, H. S. Teoh wrote: Actually, reading through DIP32 again, it sounds like Kenji is proposing the *same* syntax for both built-in tuples and std.typecons.Tuple. In the code example under "Generic type/expression tuple syntax", he refers to them respectively as "tuple type" and "tuple value". Mixing is also allowed (e.g., in the "alias Fields" line). Maybe mixing should be disallowed, then, unless your tuple was constructed from a variadic template argument list. //Error. Tuple containing types //at runtime doesn't make any sense auto tup = #(1, int); //Error auto tup = #(int, string); //Ok auto tup = #(1, "a", true); T[0] Test(T...)(T ts) { //Okay, each element of T can //be statically inspected to ensure //that you don't do something weird auto tup = T; //Also okay alias tup = T; //Error auto tup = #(int, string, bool); //Ok alias tup = #(int, string, bool); } There might be some areas where this conflation may cause trouble, ... Actually, looking at this again, it seems the problem is with the "tup = {1; x}" line. Does {1; x} in the above code mean {1; 123}, or does it mean {1; {alias of x}}? For example, if you wrote this: int x=123; auto tup = {1; x}; x++; writeln(tup); What should be the output? I'd say that x++ would not modify the x within the tuple. It's the same as: int x = 123; auto y = x; x++; writeln(y); Y is not changed, of course. However, if the tuple contains a reference type, such as a slice, then it would be modified. int[] x = [123]; auto tup = #(1, x); x[]++; //Prints #(1, [124]) writeln(tupe); Should the output change if the second line is changed to: alias tup = {1; x}; ? I don't think this should be possible. It would be the same as doing: alias two = 3; //Error Unless the tuple contained only types. Then it would be possible. alias intAndStr = #(int, string); //Ok The semantics are fairly easy when a tuple contains only values and only types. The hard part is when it contains both values AND types, so that should be severely limited, maybe only within variadic templates. I can't remember where this was mentioned... I think it was in one of the other tuple threads I linked, and I think you brought it up, Teo. auto tup = #(1, "a"); alias tupType = typeof(tup); While tup is a value, its type is #(int, string). In other words, the type of a tuple value is a TypeTuple (alias tuple, whatever). Which means the type of a tuple is a tuple itself, which is pretty nifty.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 01:06:28 UTC, H. S. Teoh wrote: Actually, reading through DIP32 again, it sounds like Kenji is proposing the *same* syntax for both built-in tuples and std.typecons.Tuple. In the code example under "Generic type/expression tuple syntax", he refers to them respectively as "tuple type" and "tuple value". Mixing is also allowed (e.g., in the "alias Fields" line). Maybe mixing should be disallowed, then, unless your tuple was constructed from a variadic template argument list. auto tup = #(1, int); //ERROR template TMP(T...) { auto tup = T; } So it sounds like this is similar to what bearophile was suggesting -- the unification of built-in tuples and Phobos Tuples. I suppose the intention is that if a built-in tuple like (1, "a", 1.0) is used as a value, it would be automatically translated into a runtime tuple value. I'm not sure if the reverse is possible, though, since if the tuple contains some runtime values, then it's not possible to translate it back into a built-in tuple. Actually, going in either direction requires some restrictions; for example, if a tuple contains a type, like (1, int), then it's impossible to translate it into a runtime tuple (types have no runtime value in and of themselves). Similarly, if a tuple contains a runtime variable, then it's impossible to use it as a compile-time tuple. But if a tuple contains only compile-time known values, then it's in theory usable both as a built-in tuple and a runtime tuple. There might be some areas where this conflation may cause trouble, though; for example, if you have a tuple (1, x) where x is a runtime variable, then should it be treated as (1, {alias of x}) or (1, {runtime value of x})? The former would happen if you pass it to a template that expects an int and and alias parameter, for example, and the latter if you try to store this tuple into a variable. It may lead to this weird situation: template Tmpl(int x, alias y) { ... } int x=123; auto tup = {1; x}; alias T = Tmpl!tup; // OK, 1 -> int x, x -> alias y auto tup2 = tup;// store {1;123} into variable alias U = Tmpl!tup2; // ERROR: cannot instantiate template with runtime variable Actually, looking at this again, it seems the problem is with the "tup = {1; x}" line. Does {1; x} in the above code mean {1; 123}, or does it mean {1; {alias of x}}? For example, if you wrote this: int x=123; auto tup = {1; x}; x++; writeln(tup); What should be the output? Should the output change if the second line is changed to: alias tup = {1; x}; ? T
Re: A Discussion of Tuple Syntax
Andrei Alexandrescu: return tuple(1, "a"); About the same syntax should work for both packing and unpacking tuples. Bye, bearophile
Re: A Discussion of Tuple Syntax
Random idea - what do you thing about merging current Tuple and TypeTuple into one and than separating them back with new and clear semantics - "run-time tuple" vs "compile-time tuple" with two distinct literals? TypeTuple and Tuple then can remain as compatibility deprecated implementations.
Re: A Discussion of Tuple Syntax
On Tue, Aug 20, 2013 at 02:40:00AM +0200, Meta wrote: > On Tuesday, 20 August 2013 at 00:29:17 UTC, H. S. Teoh wrote: > >Sounds like you're confused about what built-in tuples are (and > >you wouldn't be the first -- they're rather confusing things). > > I know the difference between std.typecons.Tuple and the built-in > tuples. What I'm confused about is I thought that the main objective > of the tuple literal syntax (destructuring/pattern matching aside) > was a means to have first-class tuples. That is, tuples that have > their own literal syntax, have a list of valid operations that can > be done on them, can be passed to functions, and can be returned > from functions. Actually, reading through DIP32 again, it sounds like Kenji is proposing the *same* syntax for both built-in tuples and std.typecons.Tuple. In the code example under "Generic type/expression tuple syntax", he refers to them respectively as "tuple type" and "tuple value". Mixing is also allowed (e.g., in the "alias Fields" line). So it sounds like this is similar to what bearophile was suggesting -- the unification of built-in tuples and Phobos Tuples. I suppose the intention is that if a built-in tuple like (1, "a", 1.0) is used as a value, it would be automatically translated into a runtime tuple value. I'm not sure if the reverse is possible, though, since if the tuple contains some runtime values, then it's not possible to translate it back into a built-in tuple. Actually, going in either direction requires some restrictions; for example, if a tuple contains a type, like (1, int), then it's impossible to translate it into a runtime tuple (types have no runtime value in and of themselves). Similarly, if a tuple contains a runtime variable, then it's impossible to use it as a compile-time tuple. But if a tuple contains only compile-time known values, then it's in theory usable both as a built-in tuple and a runtime tuple. There might be some areas where this conflation may cause trouble, though; for example, if you have a tuple (1, x) where x is a runtime variable, then should it be treated as (1, {alias of x}) or (1, {runtime value of x})? The former would happen if you pass it to a template that expects an int and and alias parameter, for example, and the latter if you try to store this tuple into a variable. It may lead to this weird situation: template Tmpl(int x, alias y) { ... } int x=123; auto tup = {1; x}; alias T = Tmpl!tup; // OK, 1 -> int x, x -> alias y auto tup2 = tup;// store {1;123} into variable alias U = Tmpl!tup2;// ERROR: cannot instantiate template with runtime variable Actually, looking at this again, it seems the problem is with the "tup = {1; x}" line. Does {1; x} in the above code mean {1; 123}, or does it mean {1; {alias of x}}? For example, if you wrote this: int x=123; auto tup = {1; x}; x++; writeln(tup); What should be the output? Should the output change if the second line is changed to: alias tup = {1; x}; ? T -- A mathematician is a device for turning coffee into theorems. -- P. Erdos
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 00:29:17 UTC, H. S. Teoh wrote: Sounds like you're confused about what built-in tuples are (and you wouldn't be the first -- they're rather confusing things). I know the difference between std.typecons.Tuple and the built-in tuples. What I'm confused about is I thought that the main objective of the tuple literal syntax (destructuring/pattern matching aside) was a means to have first-class tuples. That is, tuples that have their own literal syntax, have a list of valid operations that can be done on them, can be passed to functions, and can be returned from functions. Whether this is implemented as a syntactic sugar for std.typecons.Tuple, syntactic sugar for the built-in tuples, or a primitive type analogous to int, char, double[], etc. is not particularly important. It's becoming clear, now, that everyone has a different idea about what tuples should comprise, and I believe I have a better understanding of why this issue has yet to be solved. The answer is, this code makes no sense, because you can't return template arguments from a function. It makes as much sense as returning a function's signature from a function. You can't do that, because a function's signature isn't a runtime value that can be returned. Similarly: return TypeTuple!(int, 1); doesn't really make sense. The tuple (int, 1) isn't a runtime value that you can return from a function. It's a compile-time concept that doesn't exist at runtime. All which makes it not first-class. Let's return, then, to Kenji's DIP, and ask how the semantics he described can be implemented in D, unless someone objects to some facet of the DIP, at which point it can be worked out.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 00:28:47 UTC, Meta wrote: Yes, changing semantics is a bad thing, which is why I was originally thinking of the tuple syntax as sugar for std.typecons.Tuple. The proposed syntax takes a hit if it is just sugar for the compiler tuples. They will break in some cases when being passed to functions, and will still not be able to be returned from functions. It is not about sugar. It is about having entity in standard library to express concept that is built in into language and confusion it creates. Fixing semantics to allow _even more auto-expansion_ is a nice possible side effect. What you ask it is done by Tuple and there is nothing special about it - it is a struct, normal value type. One may be disappointed with relatively verbose syntax but it is not a real issue. But conflating it with built-in possible is simply impossible - if you even start thinking about syntax that does it, you probably need to re-read all documentation linked in this topic on tuple topic. Of course, we could have changed language in that regard - but this is a huge change, so complex that thinking about literal syntax is last thing we should do. And it does not seem to have much supporters to start with.
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 00:13:24 UTC, Dicebot wrote: No. No. Absolutely no. What you want is simply syntax sugar for std.typecons.Tuple - it is not worth any language change, contrary to semantical issues with built-in tuples. Auto-expansion and integration with function/template parameter lists is what makes D built-in tuple that useful and it should stay so with hypothetical tuple literals. Yes, changing semantics is a bad thing, which is why I was originally thinking of the tuple syntax as sugar for std.typecons.Tuple. The proposed syntax takes a hit if it is just sugar for the compiler tuples. They will break in some cases when being passed to functions, and will still not be able to be returned from functions.
Re: A Discussion of Tuple Syntax
On Tue, Aug 20, 2013 at 02:14:28AM +0200, Meta wrote: > On Tuesday, 20 August 2013 at 00:03:48 UTC, Andrei Alexandrescu > wrote: > >On 8/19/13 4:48 PM, Meta wrote: > >>I don't necessarily want built-in syntax for a library type, but > >>making tuples first-class would be nice. I mean, it's a bummer that > >>they can't be returned from functions. That should definitely be > >>changed. > > > >return tuple(1, "a"); > > That's not a TypeTuple, though, it's a built-in tuple. Sounds like you're confused about what built-in tuples are (and you wouldn't be the first -- they're rather confusing things). They are perhaps best thought of as template argument lists. As such, they have no runtime value or, as Andrej puts it, they have no ABI. So it doesn't make sense to return them from a function. What should be the meaning, for example, of: template fun(A...) { auto fun() { return A; } } ? The answer is, this code makes no sense, because you can't return template arguments from a function. It makes as much sense as returning a function's signature from a function. You can't do that, because a function's signature isn't a runtime value that can be returned. Similarly: return TypeTuple!(int, 1); doesn't really make sense. The tuple (int, 1) isn't a runtime value that you can return from a function. It's a compile-time concept that doesn't exist at runtime. T -- MACINTOSH: Most Applications Crash, If Not, The Operating System Hangs
Re: A Discussion of Tuple Syntax
On 8/19/13 5:14 PM, Meta wrote: On Tuesday, 20 August 2013 at 00:03:48 UTC, Andrei Alexandrescu wrote: On 8/19/13 4:48 PM, Meta wrote: I don't necessarily want built-in syntax for a library type, but making tuples first-class would be nice. I mean, it's a bummer that they can't be returned from functions. That should definitely be changed. return tuple(1, "a"); That's not a TypeTuple, though, it's a built-in tuple. void main() { writeln(func()); } TypeTuple!(int, string) func() { return tuple(1, "a"); //Error } Nor does it work the other way around: Tuple!(int, string) func() { return TypeTuple!(1, "a"); //Error } How would this work for some hypothetical built-in syntax? #(int, string) func() { return tuple(1, "a"); //Error? } Why would it be necessary to return an object of type TypeTuple (i.e. template tuple)? It has no state. Andrei
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 00:14:30 UTC, Meta wrote: That's not a TypeTuple, though, it's a built-in tuple. s/built-in tuple/library tuple
Re: A Discussion of Tuple Syntax
On Tuesday, 20 August 2013 at 00:03:48 UTC, Andrei Alexandrescu wrote: On 8/19/13 4:48 PM, Meta wrote: I don't necessarily want built-in syntax for a library type, but making tuples first-class would be nice. I mean, it's a bummer that they can't be returned from functions. That should definitely be changed. return tuple(1, "a"); That's not a TypeTuple, though, it's a built-in tuple. void main() { writeln(func()); } TypeTuple!(int, string) func() { return tuple(1, "a"); //Error } Nor does it work the other way around: Tuple!(int, string) func() { return TypeTuple!(1, "a"); //Error } How would this work for some hypothetical built-in syntax? #(int, string) func() { return tuple(1, "a"); //Error? }
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 23:55:32 UTC, Meta wrote: On Monday, 19 August 2013 at 23:48:36 UTC, Meta wrote: ... An addendum: void main() { //Prints 1 writeln(func(TypeTuple!(1, 2))); } int func(int i, int j) { return i; } This is bad and should never be allowed with some hypothetical tuple literal syntax. The desired behaviour is: void main() { //Error: func is not callable using argument types (#(int, int)) writeln(func(#(1, 2)); } *Unless* you use .expand/[] (pick your poison): void main() { writeln(func(#(1, 2).expand)); //Or #(1, 2)[] } No. No. Absolutely no. What you want is simply syntax sugar for std.typecons.Tuple - it is not worth any language change, contrary to semantical issues with built-in tuples. Auto-expansion and integration with function/template parameter lists is what makes D built-in tuple that useful and it should stay so with hypothetical tuple literals.
Re: A Discussion of Tuple Syntax
On 8/19/13 4:48 PM, Meta wrote: I don't necessarily want built-in syntax for a library type, but making tuples first-class would be nice. I mean, it's a bummer that they can't be returned from functions. That should definitely be changed. return tuple(1, "a"); Andrei
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 23:48:36 UTC, Meta wrote: ... An addendum: void main() { //Prints 1 writeln(func(TypeTuple!(1, 2))); } int func(int i, int j) { return i; } This is bad and should never be allowed with some hypothetical tuple literal syntax. The desired behaviour is: void main() { //Error: func is not callable using argument types (#(int, int)) writeln(func(#(1, 2)); } *Unless* you use .expand/[] (pick your poison): void main() { writeln(func(#(1, 2).expand)); //Or #(1, 2)[] }
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 18:40:58 UTC, H. S. Teoh wrote: Case in point. :) So we're actually talking at cross purposes here. Bearophile & Meta et al want native syntax for *runtime* tuples (i.e. std.typecons.Tuple -- sorry for the mixup with std.range in my earlier posts), but you're talking about native syntax for alias tuples (aka TypeTuples). Two completely different things. Now that I reread Kenji's DIP for a third time, I see/recall that his intention was for this syntax to be for alias tuples. In that case, wouldn't this necessitate a change in semantics? Will these alias tuples using built-in syntax still auto-expand? I agree that we shouldn't be making built-in syntax for a library type. If anything, any dedicated syntax should be reserved for alias tuples (aka std.typetuple.Typetuple). Or, at the very least, rename TypeTuple to AliasTuple. I don't necessarily want built-in syntax for a library type, but making tuples first-class would be nice. I mean, it's a bummer that they can't be returned from functions. That should definitely be changed. Conflating these two concepts has led to endless confusion, which is why I insisted on addressing this issue before we even begin to talk about syntax. Otherwise we're going nowhere. T
Re: A Discussion of Tuple Syntax
ixid: Are singleton tuples actually useful for anything? They are not much useful, but unless you want to outlaw them, you have to keep them in account when you design a syntax. Bye, bearophile
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 22:36:34 UTC, bearophile wrote: ixid: auto assoc_array = [1 : (2 : 3)]; // A tuple associative value auto assoc_array2 = [(1 : 2) : 3]; // A tuple associative key auto ternary = value? (1 : 2) : (3 : 4); // tuple values in a ternary operator It's nicer looking than #(). Are singleton tuples represented with (1:) ? Bye, bearophile Sure, why not? It looks happy enough. :) Are singleton tuples actually useful for anything?
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 18:43:37 UTC, Meta wrote: What does concatenating a tuple actually do? That is: auto a = #(1,2) ~ 3; //Result: a == #(1,2,3), right? auto b = a ~ #(4,5); //Is b == #(1,2,3,#(4,5)) or is b == #(1,2,3,4,5)? I think it should work the same as with arrays. So: auto a = #(1, 2) ~ 3; //Error: 3 is not a tuple auto a = #(1, 2) ~ #(3); //Result: #(1, 2, 3), just like an array auto b = a ~ #(4, 5); //Result: #(1, 2, 3, 4, 5). Again, like arrays. I was being dumb, [1, 2] ~ 3 is of course valid for arrays, so that should work for tuples as well. Furthermore, #(1, 2) ~ "a" should be valid, and will result in #(1, 2, "a"). I think keeping the same semantics as arrays would be the best way to do it. I think it nicely follows the principle of least astonishment. If you wanted to explicitly append a tuple and have it nested, you'd need to do: auto b = a ~ #(#(4, 5)); Which is messy, but at least it's explicit about what is going on. The tricky part of this that I forgot to mention is that you can't do this with arrays, so we're in unknown territory, but I think this is sensible. Again, it's quite ugly, but oh well.
Re: A Discussion of Tuple Syntax
On 2013-08-19 17:04, Meta wrote: On Monday, 19 August 2013 at 21:03:50 UTC, Andrei Alexandrescu wrote: b) syntactic support for ignoring certain members in a destructuring - is that really needed? auto (a, ?, c) = functionReturningTupleOrStaticArrayWith3Elements(); In fairness, it is very common in other languages with pattern matching/destructuring. Off the top of my head I can think of Haskell, ML, Racket, Javascript (destructuring only) and Rust. same in matlab. once destruction->auto assignment is available, (future) library functions will make heavy use of it, e.g. statistics functions returning lots of parameters/characteristics for a dataset. more often than not one is only interested in a few. /det
Re: A Discussion of Tuple Syntax
ixid: auto assoc_array = [1 : (2 : 3)]; // A tuple associative value auto assoc_array2 = [(1 : 2) : 3]; // A tuple associative key auto ternary = value? (1 : 2) : (3 : 4); // tuple values in a ternary operator It's nicer looking than #(). Are singleton tuples represented with (1:) ? Bye, bearophile
Re: A Discussion of Tuple Syntax
On Friday, 16 August 2013 at 23:48:53 UTC, bearophile wrote: Meta: Andrei/Walter didn't want to merge that pull request without a full consideration of the different design issues involved, which in retrospect was a good decision. I agree that before adding some new syntax you have to think well. - {a, b} is not as pretty, but it's not that bad of an alternative (though it may still have issues as well) It has technical issues, they were discussed in one of the last threads. Such problems should be added in a note in the DIP32, so they don't get lost in time. - #(a, b) is unambiguous and would probably be the easiest option. I don't think it looks too bad, but some people might find it ugly and noisy It looks nice (but I don't know if it's technically usable), I have added it at the end of the list of alternative syntaxes: http://wiki.dlang.org/DIP32#Use_case_of_uniform_tuple_syntax I personally think #(a, ?) or #(a, *) would be best, but all that's really necessary is a symbol that cannot also be an identifier. I think ? is clear, unambiguous, etc, and I like it. The * is already used for pointers, product, dereferencing, ** is used for pow, so it's better to not add further meanings to it. - Concatenating tuples with ~. This is nice to have, but not particularly important. In theory I like this operation, but in practice in my D code I don't need it often, so I think it should be left out, for later. In Python I sometimes concatenate tuples, but in such use cases they are essentially immutable dynamically typed arrays, losing their positional meaning. Tuples could also be used in switch/case statements, to support a very basic form of pattern matching. See also a standard method named "unapply" I have discussed a bit here, coming from Scala language, that is a very simple solution to solve a significant problem: http://d.puremagic.com/issues/show_bug.cgi?id=596 Bye, bearophile What about using : as people had intended to use the comma operator? uint a, b; (a : b) = (1 : 2); (a : b) = tupleReturningFunction; auto c = (1 : (2 : 3)); // A tuple containing a tuple auto d = (1 : ((2 : 3) : (4 : 5))); // a tuple with a tuple of tuples as a member auto assoc_array = [1 : (2 : 3)]; // A tuple associative value auto assoc_array2 = [(1 : 2) : 3]; // A tuple associative key auto ternary = value? (1 : 2) : (3 : 4); // tuple values in a ternary operator It's nicer looking than #().
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 21:03:50 UTC, Andrei Alexandrescu wrote: I'm saying that there's a mix of useful stuff and just syntactic additions that are arguably less so. In my opinion: a) destructuring tuples in auto declarations - good to have: auto (a, b, c) = functionReturningTupleOrStaticArrayWith3Elements(); b) syntactic support for ignoring certain members in a destructuring - is that really needed? auto (a, ?, c) = functionReturningTupleOrStaticArrayWith3Elements(); In fairness, it is very common in other languages with pattern matching/destructuring. Off the top of my head I can think of Haskell, ML, Racket, Javascript (destructuring only) and Rust. This syntax is more important when pattern matching, but also seems to be almost universally used in destructuring. From the DIP: switch (tup) { case {1, 2}: case {$, 2}: //Don't care what the first value is, only match on second case {1, x}: default: } You could just replace {$, 2} with {x, 2} and never use x, but this creates the problem that Bearophile mentioned. auto t1 = #(5, "hello", 1.5); //Fine auto #(_, _, x) = t1; //Dammit, can't use _ twice as it's a variable If you replace _ with throwaway variable names, it becomes much less clear (IMO) exactly what is going on. Any programmer reading your code would have to examine the function to see if the bindings introduced are ever used, or if they are just throwaways. Maybe it's unproductive to argue over the colour of the bike shed, but we need to know whether it's worth adding windows.
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 21:16:43 UTC, Brad Anderson wrote: On Monday, 19 August 2013 at 19:24:32 UTC, Timon Gehr wrote: On 08/19/2013 07:44 PM, H. S. Teoh wrote: Well, OK, whatever they're supposed to be called. Compiler-tuples, or expression tuples, or whatever. See, part of the problem is that they just don't have any good name that correctly conveys what they are. They are simply template argument lists. (http://wiki.dlang.org/The_D_Programming_Language/Seq) Nice short FAQ for one of the more confusing parts of D. The name "template argument list" clears things up quite a bit actually (but is too long for a type name). Is it your idea to use Seq instead of TypeTuple? Pretty heavy on the snark though :P
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 21:03:50 UTC, Andrei Alexandrescu wrote: I'm saying that there's a mix of useful stuff and just syntactic additions that are arguably less so. Wanted to note here that while discussing syntax at this moment is indeed mostly useless but playing with imaginary code snippets does help to understand what behavior people want to see and that can be used as a basis for formalizing semantics.
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 19:24:32 UTC, Timon Gehr wrote: On 08/19/2013 07:44 PM, H. S. Teoh wrote: Well, OK, whatever they're supposed to be called. Compiler-tuples, or expression tuples, or whatever. See, part of the problem is that they just don't have any good name that correctly conveys what they are. They are simply template argument lists. (http://wiki.dlang.org/The_D_Programming_Language/Seq) Nice short FAQ for one of the more confusing parts of D. The name "template argument list" clears things up quite a bit actually (but is too long for a type name). Is it your idea to use Seq instead of TypeTuple?
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 20:51:47 UTC, Andrei Alexandrescu wrote: Normal alias. Yah, perhaps "template tuples" are more descriptive. And how would you call _instance_ of template tuple then? (which is also built-in tuple but not template tuple)
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 20:46:02 UTC, Andrei Alexandrescu wrote: It's stuff like this that's just useless and gives a bad direction to the whole discussion. There's hardly anything wrong with auto x = t1[2] or auto gr = t1[1], but once the bikeshed is up for painting, the rainbow won't suffice. I started this discussion to build on Kenji's DIP, which discusses destructuring and pattern matching syntax in addition to tuple literal syntax, as well as the previous discussion that's already gone on in the two "DIP discussion" threads. Are you saying that you dislike the destructuring/pattern matching discussion as a whole? I'm saying that there's a mix of useful stuff and just syntactic additions that are arguably less so. In my opinion: a) destructuring tuples in auto declarations - good to have: auto (a, b, c) = functionReturningTupleOrStaticArrayWith3Elements(); b) syntactic support for ignoring certain members in a destructuring - is that really needed? auto (a, ?, c) = functionReturningTupleOrStaticArrayWith3Elements(); Andrei
Re: A Discussion of Tuple Syntax
Andrei Alexandrescu: It's stuff like this that's just useless and gives a bad direction to the whole discussion. There's hardly anything wrong with auto x = t1[2] or auto gr = t1[1], but once the bikeshed is up for painting, the rainbow won't suffice. I was just explaining why _ can't be used as wildcard (unless you also make _ a not valid variable name), it wasn't meant to be an example of why wildcards are useful in the first place. Bye, bearophile
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 20:46:02 UTC, Andrei Alexandrescu wrote: It's stuff like this that's just useless and gives a bad direction to the whole discussion. There's hardly anything wrong with auto x = t1[2] or auto gr = t1[1], but once the bikeshed is up for painting, the rainbow won't suffice. I started this discussion to build on Kenji's DIP, which discusses destructuring and pattern matching syntax in addition to tuple literal syntax, as well as the previous discussion that's already gone on in the two "DIP discussion" threads. Are you saying that you dislike the destructuring/pattern matching discussion as a whole?
Re: A Discussion of Tuple Syntax
On 8/19/13 1:41 PM, Dicebot wrote: On Monday, 19 August 2013 at 20:36:10 UTC, Andrei Alexandrescu wrote: On 8/19/13 11:14 AM, Dicebot wrote: On Monday, 19 August 2013 at 18:11:34 UTC, Andrei Alexandrescu wrote: I'd call them alias tuples. Because we don't have strict definition of alias too? :) I'm thinking such a tuple may hold everything that one may define an alias to. Normal alias or template alias parameter? ;) Normal alias. Yah, perhaps "template tuples" are more descriptive. Andrei
Re: A Discussion of Tuple Syntax
On 8/19/13 12:54 PM, bearophile wrote: Meta: It *could* be an underscore; the only thing is that the underscore is a valid variable name, so the above expression would actually be binding two variables, which might surprise someone who was expecting otherwise. I don't really care all that much, but it's something to think about. You can't define a variable more than once in a scope, so this can't be valid: void main() { auto t1 = #(5, "hello", 1.5); auto (_, _, x) = t1; auto (_, gr, _) = t1; } While ? defines nothing, so this is OK: void main() { auto t1 = #(5, "hello", 1.5); auto (?, ?, x) = t1; auto (?, gr, ?) = t1; } Bye, bearophile It's stuff like this that's just useless and gives a bad direction to the whole discussion. There's hardly anything wrong with auto x = t1[2] or auto gr = t1[1], but once the bikeshed is up for painting, the rainbow won't suffice. Andrei
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 20:36:10 UTC, Andrei Alexandrescu wrote: On 8/19/13 11:14 AM, Dicebot wrote: On Monday, 19 August 2013 at 18:11:34 UTC, Andrei Alexandrescu wrote: I'd call them alias tuples. Because we don't have strict definition of alias too? :) I'm thinking such a tuple may hold everything that one may define an alias to. Normal alias or template alias parameter? ;) Because those two are different :P Former can't take lambda literals. Latter won't accept built-in types like int. But built-in tuple is fine with both. "T..." is a _very_ special thing in D.
Re: A Discussion of Tuple Syntax
On 8/19/13 11:54 AM, Meta wrote: Realistically, Andrei, how amenable are you and Walter to adding tuple literal/packing&unpacking/pattern matching syntax to D, be it Tuple, TypeTuple, whatever? I don't recall either of you commenting much in the two other discussion threads linked. We can discuss this all day, but it what are the actual chances of you agreeing to such a large change in the language? We'd be fools to reject a valuable addition to the language. That being said, there's no broad agreement on what's needed and what's to be added. Furthermore, most discussions are disproportionally focused on syntax (as opposed to semantics) and fall for syntactic cutesies instead of simple and robust library support. That in my experience is a bad omen. Now _that_ being said, if and when a gem comes about, we hope to have the insight to see it. Andrei
Re: A Discussion of Tuple Syntax
On 8/19/13 11:14 AM, Dicebot wrote: On Monday, 19 August 2013 at 18:11:34 UTC, Andrei Alexandrescu wrote: I'd call them alias tuples. Because we don't have strict definition of alias too? :) I'm thinking such a tuple may hold everything that one may define an alias to. Andrei
Re: A Discussion of Tuple Syntax
void main() { auto t1 = #(5, "hello", 1.5); auto (_, _, x) = t1; auto (_, gr, _) = t1; } I need to get used to the proposed syntax, sorry: void main() { auto t1 = #(5, "hello", 1.5); auto #(_, _, x) = t1; auto #(_, gr, _) = t1; } Bye, bearophile
Re: A Discussion of Tuple Syntax
On Monday, 19 August 2013 at 19:54:43 UTC, bearophile wrote: ... That too.