digitalmars.D - Tuples citizenship
- bearophile (128/129) Mar 01 2012 I think std.typecons.Tuples merit to be a little more citizens in D and ...
- Jonathan M Davis (9/18) Mar 01 2012 [snip]
- deadalnix (13/31) Mar 01 2012 I don't think out parameter is a great idea. This is rather confusing.
- Jonathan M Davis (15/55) Mar 01 2012 When you're looking to mutate existing variables in the caller, using ou...
- kennytm (6/23) Mar 01 2012 You use 'ref' to mutate existing variable, not 'out'. Multiple return va...
- bearophile (17/30) Mar 01 2012 out arguments have two risks:
- Jonathan M Davis (28/37) Mar 01 2012 Yes, but chaining functions is the issue. It doesn't work well with tupl...
- bearophile (8/24) Mar 01 2012 I have assumed to use a sane tuple unpacking syntax. So the second part ...
- Dmitry Olshansky (20/42) Mar 01 2012 just stick in .expand ?
- Jonathan M Davis (10/27) Mar 02 2012 That's assuming that you're passing all of the pieces of the tuple to th...
- kennytm (10/20) Mar 02 2012 How does 'out' make chaining any easier? Suppose we have a `R3
- Jonathan M Davis (8/32) Mar 02 2012 True, you can't chain using the out parameters, but you _can_ chain usin...
- kennytm (8/42) Mar 02 2012 You can just chain with
- Jonathan M Davis (7/18) Mar 02 2012 If the function uses out, then you can chain the return value without lo...
- deadalnix (3/21) Mar 02 2012 But you are assigning to a variable, you just declare it before.
- kennytm (10/30) Mar 02 2012 I see what you mean. However, this is useful only when you know one of t...
- Jonathan M Davis (7/41) Mar 02 2012 It's not like using out is fantastic and tuple sucks. They both have pro...
- kennytm (10/17) Mar 02 2012 bearophile's example (frexp, remquo) are good examples where tuple retur...
- Andrej Mitrovic (5/5) Mar 02 2012 Has anyone seen my recent thread about using the if+auto feature and
- renoX (12/22) Mar 02 2012 Sure, but due to D's syntax which doesn't distinguish in/out/ref
- deadalnix (2/19) Mar 02 2012 You got it right. +1
- deadalnix (14/51) Mar 02 2012 Do you rally think that shorter is always better ? I don't think so. I
- deadalnix (5/12) Mar 02 2012 I'd argue that not mutating parameter result in cleaner code most of the...
- Jonathan M Davis (18/33) Mar 02 2012 the
- bearophile (11/15) Mar 01 2012 Are you able to show me one or more examples where using one or more out...
- kennytm (9/16) Mar 01 2012 (snip)
- Don (2/10) Mar 02 2012 They're defined that way because they come from C, and they're in IEEE75...
I think std.typecons.Tuples merit to be a little more citizens in D and Phobos. I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple: pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int) frexp(real value); nothrow trusted real remquo(real x, real y, out int n); ==> nothrow trusted Tuple!(real, int) remquo(real x, real y); ------------------------ It's good for tuples to become more common in D code. Some time ago I have asked the built-in associative arrays to grow a method to iterate on key-value pairs, named "byPair": http://d.puremagic.com/issues/show_bug.cgi?id=5466 Andrei answered:byPair is tricky because std.tuple is not visible from object.How do you solve this problem? Now I think about a "pairs" method too: int[string] aa; Tuple!(string, int) ps = aa.pairs; foreach (p; aa.byPair) assert(is( typeof(p) == Tuple!(string, int) )); This example shows an use case of byItem. Given a string, the task here is to show the char frequencies, putting higher frequencies on top, and sorting alphabetically the chars that share the same frequency. A Python 2.6 program that solves the problem: from collections import defaultdict text = "the d programming language is an object oriented " + \ "imperative multi paradigm system programming " + \ "language created by walter bright of digital mars" frequences = defaultdict(int) for c in text: frequences[c] += 1 pairs = sorted(frequences.iteritems(), key=lambda (c,f): (-f,c)) for (c, f) in pairs: print f, c The output of the Python program: 20 14 a 12 e 11 g 11 i 11 r 10 t 9 m 6 n 5 d 5 l 5 o 4 p 4 s 3 b 3 u 2 c 2 h 2 y 1 f 1 j 1 v 1 w Three different solutions in D (probably there are other solutions, maybe even better ones) using Phobos: import std.stdio, std.typecons, std.algorithm, std.array; void main() { auto text = "the d programming language is an object oriented " ~ "imperative multi paradigm system programming " ~ "language created by walter bright of digital mars"; int[char] frequences; foreach (char c; text) frequences[c]++; Tuple!(int,char)[] pairs1 = array(map!(c => tuple(frequences[c], c))(frequences.byKey)); schwartzSort!(p => tuple(-p[0], p[1]))(pairs1); foreach (pair; pairs1) writeln(pair[0], " ", pair[1]); writeln(); import std.conv; dchar[] keys = to!(dchar[])(frequences.keys); schwartzSort!(c => tuple(-frequences[cast(char)c], c))(keys); foreach (dchar c; keys) writeln(frequences[cast(char)c], " ", c); writeln(); Tuple!(int,char)[] pairs1b = array(map!(c => tuple(-frequences[c], c))(frequences.byKey)); sort(pairs1b); foreach (pair; pairs1b) writeln(-pair[0], " ", pair[1]); writeln(); } A version using AA.pairs (or array(AA.byPair)), I have not used 'auto' for type clarity: Tuple!(char,int)[] pairs2 = frequences.pairs; schwartzSort!(c_f => tuple(-c_f[1], c_f[0]))(pairs2); foreach (c_f; pairs2) writeln(c_f[1], " ", c_f[0]); I am now writing a good amount of D2 code, some of it is functional style, or it's just translated from Python, and one annoying thing that comes out quite frequently is the lack of syntax to unpack a tuple into some variables. If you want a short self-contained example, this is a small program (and it's not much functional. Tuples aren't just for functional-style code): http://rosettacode.org/wiki/Sokoban#D It contains code like: immutable dirs = [tuple( 0, -1, 'u', 'U'), tuple( 1, 0, 'r', 'R'), tuple( 0, 1, 'd', 'D'), tuple(-1, 0, 'l', 'L')]; // ... foreach (di; dirs) { CTable temp = cur; immutable int dx = di[0]; immutable int dy = di[1]; With tuple unpacking becomes: foreach (di; dirs) { CTable temp = cur; immutable (dx, dy) = di.slice!(0, 2); I don't know if this is going to work, because di[0..2] creates a 2-typetuple! foreach (di; dirs) { CTable temp = cur; immutable (dx, dy) = di[0 .. 2]; Maybe this is not efficient: foreach (di; dirs) { CTable temp = cur; immutable (dx, dy, _1, _2) = di; Another example from that little program: alias Tuple!(CTable, string, int, int) Four; GrowableCircularQueue!Four open; // ... while (open.length) { immutable item = open.pop(); immutable CTable cur = item[0]; immutable string cSol = item[1]; immutable int x = item[2]; immutable int y = item[3]; With a tuple unpacking syntax becomes: while (open.length) { immutable (cur, cSol, x, y) = open.pop(); While I use tuples I hit similar situations often. For people interested in trying this idea, there is a patch by Kenji Hara (one or two parts are missing, like tuple unpacking in a foreach(...) and in function signatures, but I think most meat is already present): https://github.com/D-Programming-Language/dmd/pull/341 Bye, bearophile
Mar 01 2012
On Thursday, March 01, 2012 17:08:44 bearophile wrote:I think std.typecons.Tuples merit to be a little more citizens in D and Phobos. I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple: pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int) frexp(real value);[snip] Having good tuple support is great, but out parameters are great too. I'm sure that there are plenty of cases where using out parameters is actually far cleaner than using tuples, since you don't have multiple return values to deal with. So, better enabling tuples is great, but I don't think that we're necessarily moving in a good direction if we're trying to eliminate out parameters in favor of tuples. - Jonathan M Davis
Mar 01 2012
Le 01/03/2012 23:33, Jonathan M Davis a écrit :On Thursday, March 01, 2012 17:08:44 bearophile wrote:I don't think out parameter is a great idea. This is rather confusing. I tend to think as function's parameter as input of the function and return value as an output. Books usualy agree, so I guess it is a valid point. OOP give us another parameter to play with : this. It act as a state that can be modified. UFCS is nice to extends that outside OOP. This is a way better alternative than out parameter. Tuples are nice too. Since we have auto, this isn't a big deal. Both should be preferred to out parameters IMO, because the later cannot be differentiated at the calling point and force the programer to refers to the function declaration all the time or use its - limited and sometime inaccurate - memory. This is something we want to avoid.I think std.typecons.Tuples merit to be a little more citizens in D and Phobos. I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple: pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int) frexp(real value);[snip] Having good tuple support is great, but out parameters are great too. I'm sure that there are plenty of cases where using out parameters is actually far cleaner than using tuples, since you don't have multiple return values to deal with. So, better enabling tuples is great, but I don't think that we're necessarily moving in a good direction if we're trying to eliminate out parameters in favor of tuples. - Jonathan M Davis
Mar 01 2012
On Friday, March 02, 2012 00:05:22 deadalnix wrote:Le 01/03/2012 23:33, Jonathan M Davis a écrit :When you're looking to mutate existing variables in the caller, using out parameters results in cleaner code. Tuples are inherently messier, because you have to deal with multiple return values. They also often do poorly when you need to use the functional programming, because often you want all of the return values for later use but only want to pass _one_ of them to the function that you're passing the result to. At other times, tuples are nicer - like when you actually _want_ the return value packed together (though often, if that's what you really want, a struct might be better). And if you're not looking to assign the parts of a tuple to existing variables, then they're not quite as bad as when you are, since you don't necessarily have to then assign the pieces to other variables. Both have value, though if you need a lot of either, you should probably consider whether a struct or class would suit what you're doing better. - Jonathan M DavisOn Thursday, March 01, 2012 17:08:44 bearophile wrote:I don't think out parameter is a great idea. This is rather confusing. I tend to think as function's parameter as input of the function and return value as an output. Books usualy agree, so I guess it is a valid point. OOP give us another parameter to play with : this. It act as a state that can be modified. UFCS is nice to extends that outside OOP. This is a way better alternative than out parameter. Tuples are nice too. Since we have auto, this isn't a big deal. Both should be preferred to out parameters IMO, because the later cannot be differentiated at the calling point and force the programer to refers to the function declaration all the time or use its - limited and sometime inaccurate - memory. This is something we want to avoid.I think std.typecons.Tuples merit to be a little more citizens in D and Phobos. I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple: pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int) frexp(real value);[snip] Having good tuple support is great, but out parameters are great too. I'm sure that there are plenty of cases where using out parameters is actually far cleaner than using tuples, since you don't have multiple return values to deal with. So, better enabling tuples is great, but I don't think that we're necessarily moving in a good direction if we're trying to eliminate out parameters in favor of tuples. - Jonathan M Davis
Mar 01 2012
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:When you're looking to mutate existing variables in the caller, using out parameters results in cleaner code. Tuples are inherently messier, because you have to deal with multiple return values. They also often do poorly when you need to use the functional programming, because often you want all of the return values for later use but only want to pass _one_ of them to the function that you're passing the result to.You use 'ref' to mutate existing variable, not 'out'. Multiple return value doesn't replace the use case of ref. Not sure how messy it is to extract one value out of the tuple. It is much more messier to ignore an 'out' parameter than a field in a tuple in the call site.At other times, tuples are nicer - like when you actually _want_ the return value packed together (though often, if that's what you really want, a struct might be better). And if you're not looking to assign the parts of a tuple to existing variables, then they're not quite as bad as when you are, since you don't necessarily have to then assign the pieces to other variables. Both have value, though if you need a lot of either, you should probably consider whether a struct or class would suit what you're doing better. - Jonathan M Davis
Mar 01 2012
Jonathan M Davis:When you're looking to mutate existing variables in the caller, using out parameters results in cleaner code. Tuples are inherently messier, because you have to deal with multiple return values.out arguments have two risks: - If you assign a value to a variable and then use it to call a function, the precedent value is ignored and overwritten. - If in a function you forget to assign an out argument, the D compiler produces no errors. Both source of bugs are not present with tuples.They also often do poorly when you need to use the functional programming,As you know functional languages use tuples all the time.At other times, tuples are nicer - like when you actually _want_ the return value packed together (though often, if that's what you really want, a struct might be better).Defining a struct makes your code messier. D tuples support named fields too, so the advantage of using a struct is limited.And if you're not looking to assign the parts of a tuple to existing variables, then they're not quite as bad as when you are, since you don't necessarily have to then assign the pieces to other variables.There are parts of your post that I don't fully understand.Both have value, though if you need a lot of either, you should probably consider whether a struct or class would suit what you're doing better.For most usages of a tuple a class instance means useless heap activity and more work for the GC. Using a struct to return the results of a function is sometimes acceptable, but most times I don't use tuples for that purpose. Consider this code in my original post, defining two static structs doesn't do much good to such kind of code: Tuple!(char,int)[] pairs2 = frequences.pairs; schwartzSort!(c_f => tuple(-c_f[1], c_f[0]))(pairs2); foreach (c_f; pairs2) writeln(c_f[1], " ", c_f[0]); Tuples are often defined and used on the fly, in-place. Bye, bearophile
Mar 01 2012
On Thursday, March 01, 2012 18:57:15 bearophile wrote:Jonathan M Davis:Yes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all. You're forced to assign the tuple to something else and then call then function rather than chain calls. That's one of the reasons that you constantly end up using stuff like let expressions and pattern matching in functional languages. You don't _want_ a tuple. Dealing with a tuple is annoying. It's just that it's often the best tool that you have to pass disparate stuff around, so that's what you use.They also often do poorly when you need to use the functional programming,As you know functional languages use tuples all the time.It is often really annoying to have to deal with tuple return values, because you have to worry about unpacking the result. I don't want to use a tuple in the caller. Tuples are generally for grouping unrelated data that you don't necessarily want to keep togother (since if you did, you'd generally use a struct). I want the result to actually be assigned to variables. That is definitely cleaner with out than with tuples. int exp; auto result = frexp(value, exp); vs auto tup = frexp(value); result = tup[0]; exp = tup[1]; Getting tuple return values is annoying. Yes, it can be useful, but most stuff doesn't operate on tuples. It operates on the pieces of tuples. So, you have to constantly break them up. So, using out results in much nicer code. It always feels like I'm fighting the code when I have to deal with tuple return values. - Jonathan M DavisAnd if you're not looking to assign the parts of a tuple to existing variables, then they're not quite as bad as when you are, since you don't necessarily have to then assign the pieces to other variables.There are parts of your post that I don't fully understand.
Mar 01 2012
Jonathan M Davis:Yes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all. You're forced to assign the tuple to something else and then call then function rather than chain calls.In the years I have used a mountain of tuples in Python, but I barely perceive that problem, so I think it's not so bad.int exp; auto result = frexp(value, exp); vs auto tup = frexp(value); result = tup[0]; exp = tup[1];I have assumed to use a sane tuple unpacking syntax. So the second part of your comparison is: immutable (result, exp) = frexp(value); That is better than your version with the out argument, safer, looks better, and you are even able to make both results constant.Getting tuple return values is annoying. Yes, it can be useful, but most stuff doesn't operate on tuples. It operates on the pieces of tuples. So, you have to constantly break them up. So, using out results in much nicer code.I think that the tuple unpacking syntax is able to avoid part of your problems. The point of my original post, that maybe was lost in the bulk of the text, was that an unpacking syntax sugar is very useful if you want to use tuples for real in D. Bye and thank you, bearophile
Mar 01 2012
On 02.03.2012 6:06, bearophile wrote:Jonathan M Davis:just stick in .expand ? void f(int x, int y){ } void main() { Tuple!(int, int) a; f(a.expand); } BTW it's nowhere to be found here http://dlang.org/phobos/std_typecons.html You'reYes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all.+1forced to assign the tuple to something else and then call then function rather than chain calls.In the years I have used a mountain of tuples in Python, but I barely perceive that problem, so I think it's not so bad.int exp; auto result = frexp(value, exp); vs auto tup = frexp(value); result = tup[0]; exp = tup[1];I have assumed to use a sane tuple unpacking syntax. So the second part of your comparison is: immutable (result, exp) = frexp(value); That is better than your version with the out argument, safer, looks better, and you are even able to make both results constant.s/part/most I'd say that results clearly belond to the left side of x = fun(...) expression, and tuples + unpack syntax are the way to make it consistent. With all sugar going on around Tuples, e.g. .tupleof, unpacking, I can't help but wonder why are they not built-ins. At least they should go to object.d/druntime like AA do. -- Dmitry OlshanskyGetting tuple return values is annoying. Yes, it can be useful, but most stuff doesn't operate on tuples. It operates on the pieces of tuples. So, you have to constantly break them up. So, using out results in much nicer code.I think that the tuple unpacking syntax is able to avoid part of your problems.
Mar 01 2012
On Friday, March 02, 2012 11:41:16 Dmitry Olshansky wrote:On 02.03.2012 6:06, bearophile wrote:That's assuming that you're passing all of the pieces of the tuple to the function. Often, that's not the case at all. Take the findSplit trio, for instance. What are the odds that you're going to want to pass all of the elements in the tuples that any of the return to another function? About zero, I'd say. It's _much_ more likely that you're going to want to take the results and then pass _one_ of them to another function. So, as it stands, chaining with those functions just doesn't work unless you only care about one of the results in the tuple. - Jonathan M DavisJonathan M Davis:just stick in .expand ? void f(int x, int y){ } void main() { Tuple!(int, int) a; f(a.expand); }Yes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all.
Mar 02 2012
Jonathan M Davis <jmdavisProg gmx.com> wrote:That's assuming that you're passing all of the pieces of the tuple to the function. Often, that's not the case at all. Take the findSplit trio, for instance. What are the odds that you're going to want to pass all of the elements in the tuples that any of the return to another function? About zero, I'd say. It's _much_ more likely that you're going to want to take the results and then pass _one_ of them to another function. So, as it stands, chaining with those functions just doesn't work unless you only care about one of the results in the tuple. - Jonathan M DavisHow does 'out' make chaining any easier? Suppose we have a `R3 findSplit2(R1, R2, out R4, out R5)`, how to chain if we want to pass the R4 to another function? R5 ignored; R4 theRange; findSplit2(haystack, needle, theRange, ignored); return doSomething(theRange); vs return doSomething(findSplit(haystack, needle)[1]);
Mar 02 2012
On Friday, March 02, 2012 09:31:14 kennytm wrote:Jonathan M Davis <jmdavisProg gmx.com> wrote:True, you can't chain using the out parameters, but you _can_ chain using the return value, whereas if you have a tuple, you can't chain _at all_ unless you actually need all of the returned values (either as a tuple or expanded) or if you only need _one_ of the returned values, in which case you can use the subscript operator. So, you can definitely chain better without a tuple than with. - Jonathan M DavisThat's assuming that you're passing all of the pieces of the tuple to the function. Often, that's not the case at all. Take the findSplit trio, for instance. What are the odds that you're going to want to pass all of the elements in the tuples that any of the return to another function? About zero, I'd say. It's _much_ more likely that you're going to want to take the results and then pass _one_ of them to another function. So, as it stands, chaining with those functions just doesn't work unless you only care about one of the results in the tuple. - Jonathan M DavisHow does 'out' make chaining any easier? Suppose we have a `R3 findSplit2(R1, R2, out R4, out R5)`, how to chain if we want to pass the R4 to another function? R5 ignored; R4 theRange; findSplit2(haystack, needle, theRange, ignored); return doSomething(theRange); vs return doSomething(findSplit(haystack, needle)[1]);
Mar 02 2012
Jonathan M Davis <jmdavisProg gmx.com> wrote:On Friday, March 02, 2012 09:31:14 kennytm wrote:You can just chain with return doSomething(findSplit(haystack, needle)[0]); if you just need the return value. Compare with 'out': R4 ignored; R5 ignored2; return doSomething(findSplit(haystack, needle, ignored, ignored2)); How do you chain with _partial_ amount of return values with 'out'?Jonathan M Davis <jmdavisProg gmx.com> wrote:True, you can't chain using the out parameters, but you _can_ chain using the return value, whereas if you have a tuple, you can't chain _at all_ unless you actually need all of the returned values (either as a tuple or expanded) or if you only need _one_ of the returned values, in which case you can use the subscript operator. So, you can definitely chain better without a tuple than with. - Jonathan M DavisThat's assuming that you're passing all of the pieces of the tuple to the function. Often, that's not the case at all. Take the findSplit trio, for instance. What are the odds that you're going to want to pass all of the elements in the tuples that any of the return to another function? About zero, I'd say. It's _much_ more likely that you're going to want to take the results and then pass _one_ of them to another function. So, as it stands, chaining with those functions just doesn't work unless you only care about one of the results in the tuple. - Jonathan M DavisHow does 'out' make chaining any easier? Suppose we have a `R3 findSplit2(R1, R2, out R4, out R5)`, how to chain if we want to pass the R4 to another function? R5 ignored; R4 theRange; findSplit2(haystack, needle, theRange, ignored); return doSomething(theRange); vs return doSomething(findSplit(haystack, needle)[1]);
Mar 02 2012
On Friday, March 02, 2012 09:53:19 kennytm wrote:You can just chain with return doSomething(findSplit(haystack, needle)[0]); if you just need the return value. Compare with 'out': R4 ignored; R5 ignored2; return doSomething(findSplit(haystack, needle, ignored, ignored2)); How do you chain with _partial_ amount of return values with 'out'?If the function uses out, then you can chain the return value without losing the values which were assigned to the out arguments, but if you have a tuple, and you select one of the elements in the tuple to chain, you lose the others. The only way to get _all_ of the values in the tuple is to assign the tuple to a variable, in which case, you can't chain at all. - Jonathan M Davis
Mar 02 2012
Le 02/03/2012 11:10, Jonathan M Davis a écrit :On Friday, March 02, 2012 09:53:19 kennytm wrote:But you are assigning to a variable, you just declare it before. Additionally, you loose all possibility to use auto.You can just chain with return doSomething(findSplit(haystack, needle)[0]); if you just need the return value. Compare with 'out': R4 ignored; R5 ignored2; return doSomething(findSplit(haystack, needle, ignored, ignored2)); How do you chain with _partial_ amount of return values with 'out'?If the function uses out, then you can chain the return value without losing the values which were assigned to the out arguments, but if you have a tuple, and you select one of the elements in the tuple to chain, you lose the others. The only way to get _all_ of the values in the tuple is to assign the tuple to a variable, in which case, you can't chain at all. - Jonathan M Davis
Mar 02 2012
Jonathan M Davis <jmdavisProg gmx.com> wrote:On Friday, March 02, 2012 09:53:19 kennytm wrote:I see what you mean. However, this is useful only when you know one of the return value is special, and make the rest 'out' parameters, e.g. File openFile(string fn, string mode, out ErrorCode errCode); because the API designer know people seldom focus on the 'errCode'. But if not all return values are considered unimportant (such as findSplit and remquo), randomly making some of them as the 'out' parameter just make the normal use cases more messy. Besides, you can't use type inference with 'out' parameters. You don't actually know the types R4 and R5 in my findSplit2 example.You can just chain with return doSomething(findSplit(haystack, needle)[0]); if you just need the return value. Compare with 'out': R4 ignored; R5 ignored2; return doSomething(findSplit(haystack, needle, ignored, ignored2)); How do you chain with _partial_ amount of return values with 'out'?If the function uses out, then you can chain the return value without losing the values which were assigned to the out arguments, but if you have a tuple, and you select one of the elements in the tuple to chain, you lose the others. The only way to get _all_ of the values in the tuple is to assign the tuple to a variable, in which case, you can't chain at all. - Jonathan M Davis
Mar 02 2012
On Friday, March 02, 2012 12:59:43 kennytm wrote:Jonathan M Davis <jmdavisProg gmx.com> wrote:It's not like using out is fantastic and tuple sucks. They both have pros and cons. My point is that it's not the case that always returning tuples is better, which seems to be the point that Bearophile is trying to push. There _are_ downsides to returning tuples. Whether a tuple or an out parameter is better depends on the function and the context in which it is used. - Jonathan M DavisOn Friday, March 02, 2012 09:53:19 kennytm wrote:I see what you mean. However, this is useful only when you know one of the return value is special, and make the rest 'out' parameters, e.g. File openFile(string fn, string mode, out ErrorCode errCode); because the API designer know people seldom focus on the 'errCode'. But if not all return values are considered unimportant (such as findSplit and remquo), randomly making some of them as the 'out' parameter just make the normal use cases more messy. Besides, you can't use type inference with 'out' parameters. You don't actually know the types R4 and R5 in my findSplit2 example.You can just chain with return doSomething(findSplit(haystack, needle)[0]); if you just need the return value. Compare with 'out': R4 ignored; R5 ignored2; return doSomething(findSplit(haystack, needle, ignored, ignored2)); How do you chain with _partial_ amount of return values with 'out'?If the function uses out, then you can chain the return value without losing the values which were assigned to the out arguments, but if you have a tuple, and you select one of the elements in the tuple to chain, you lose the others. The only way to get _all_ of the values in the tuple is to assign the tuple to a variable, in which case, you can't chain at all. - Jonathan M Davis
Mar 02 2012
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:It's not like using out is fantastic and tuple sucks. They both have pros and cons. My point is that it's not the case that always returning tuples is better, which seems to be the point that Bearophile is trying to push. There _are_ downsides to returning tuples. Whether a tuple or an out parameter is better depends on the function and the context in which it is used. - Jonathan M Davisbearophile's example (frexp, remquo) are good examples where tuple return is better than 'out' parameters though. Another Phobos function, I think, which should use 'out' instead of Tuples is: - std.file.getTimes (have the two 'out' parameters, and the function itself returns 'void'!) While some functions should remain using 'out': - std.stream.InputStream.read (due to overloading) And of course the extern(C) functions need to use 'out' instead of Tuple return :)
Mar 02 2012
Has anyone seen my recent thread about using the if+auto feature and opCast(bool)? It's not related to tuples but I thought it was a cool D feature. I admit I named the thread a pretty stupid name, but it was 6 AM when I posted it :) (http://forum.dlang.org/thread/mailman.195.1330399006.24984.digitalmars-d puremagic.com)
Mar 02 2012
On Friday, 2 March 2012 at 10:13:17 UTC, Jonathan M Davis wrote:If the function uses out, then you can chain the return value without losing the values which were assigned to the out arguments, but if you have a tuple, and you select one of the elements in the tuple to chain, you lose the others. The only way to get _all_ of the values in the tuple is to assign the tuple to a variable, in which case, you can't chain at all. - Jonathan M DavisSure, but due to D's syntax which doesn't distinguish in/out/ref params in a function call, this is quite confusing to read: when you read f(x,y) which is in, which is out? If D's syntax was f(x, y) ( to distinguish out or ref parameter), this would be easy to read, but this isn't the case. At least with tuples you don't have this issue. Not that I consider tuples always the good answer: for the common use case where you want to return an error code and a result, the Maybe "monad" is better.. BR, renoX
Mar 02 2012
Le 02/03/2012 03:06, bearophile a écrit :Jonathan M Davis:You got it right. +1Yes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all. You're forced to assign the tuple to something else and then call then function rather than chain calls.In the years I have used a mountain of tuples in Python, but I barely perceive that problem, so I think it's not so bad.int exp; auto result = frexp(value, exp); vs auto tup = frexp(value); result = tup[0]; exp = tup[1];I have assumed to use a sane tuple unpacking syntax. So the second part of your comparison is: immutable (result, exp) = frexp(value);
Mar 02 2012
Le 02/03/2012 02:10, Jonathan M Davis a écrit :On Thursday, March 01, 2012 18:57:15 bearophile wrote:Do you rally think that shorter is always better ? I don't think so. I think better is what the piece of code do pretty much what you expect it to do. And most of the time, I want to pass argument to a function not return value throw arguments. At the callee place, version look just like any other function call, but exp isn't a parameter, it is actually a returned value. You cannot know that reading the code, you need to know about frexp declaration - thus you scroll more source code/documentation or rely on your memory that will sometime fail. To emphasis that, just imagine the same scenario with a function that isn't from the standard lib. This practice will confuse a newcomer that don't know the stdlib well, and, used in thrid party code, will confuse anyone expect the one that write that piece of code.Jonathan M Davis:Yes, but chaining functions is the issue. It doesn't work well with tuples unless the function you're passing the result to wants the tuple. If all it wants is one piece of the tuple, then that doesn't work well at all. You're forced to assign the tuple to something else and then call then function rather than chain calls. That's one of the reasons that you constantly end up using stuff like let expressions and pattern matching in functional languages. You don't _want_ a tuple. Dealing with a tuple is annoying. It's just that it's often the best tool that you have to pass disparate stuff around, so that's what you use.They also often do poorly when you need to use the functional programming,As you know functional languages use tuples all the time.It is often really annoying to have to deal with tuple return values, because you have to worry about unpacking the result. I don't want to use a tuple in the caller. Tuples are generally for grouping unrelated data that you don't necessarily want to keep togother (since if you did, you'd generally use a struct). I want the result to actually be assigned to variables. That is definitely cleaner with out than with tuples. int exp; auto result = frexp(value, exp); vs auto tup = frexp(value); result = tup[0]; exp = tup[1]; Getting tuple return values is annoying. Yes, it can be useful, but most stuff doesn't operate on tuples. It operates on the pieces of tuples. So, you have to constantly break them up. So, using out results in much nicer code. It always feels like I'm fighting the code when I have to deal with tuple return values. - Jonathan M DavisAnd if you're not looking to assign the parts of a tuple to existing variables, then they're not quite as bad as when you are, since you don't necessarily have to then assign the pieces to other variables.There are parts of your post that I don't fully understand.
Mar 02 2012
Le 02/03/2012 00:09, Jonathan M Davis a écrit :When you're looking to mutate existing variables in the caller, using out parameters results in cleaner code.I'd argue that not mutating parameter result in cleaner code most of the time.Tuples are inherently messier, because you have to deal with multiple return values. They also often do poorly when you need to use the functional programming, because often you want all of the return values for later use but only want to pass _one_ of them to the function that you're passing the result to.The first time I encountered tuple was using Caml. This claim doesn't support my practical experience.
Mar 02 2012
On Friday, March 02, 2012 10:27:02 deadalnix wrote:Le 02/03/2012 00:09, Jonathan M Davis a =C3=A9crit :ng outWhen you're looking to mutate existing variables in the caller, usi=theparameters results in cleaner code.=20 I'd argue that not mutating parameter result in cleaner code most of =time. =20whenTuples are inherently messier, because you have to deal with multiple return values. They also often do poorly=all ofyou need to use the functional programming, because often you want =tothe return values for later use but only want to pass _one_ of them=the function that you're passing the result to.=20 The first time I encountered tuple was using Caml. This claim doesn't=support my practical experience.Yes. Functional languages use tuples. But I'm talking about chaining fu= nctions=20 like you would in a functional language. But functional languages use p= attern=20 matching and other stuff to make it not as big a problem as it is in D,= and=20 often in functional languages, you _still_ have to do their equivalent = of=20 assigning the pieces of the tuple to variables. Tuples are a bane of fu= nction=20 chaining. - Jonathan M Davis
Mar 02 2012
Jonathan M Davis:I'm sure that there are plenty of cases where using out parameters is actually far cleaner than using tuples, since you don't have multiple return values to deal with.Are you able to show me one or more examples where using one or more out arguments is in your opinion more clear (and safer!) than using a tuple with the proposed unpacking syntax? With a tuple you have to deal with multiple return values, but the semantics is cleaner (function => resulting tuple). The number of variables doesn't change, because with "out" you need to define them any way, before the call. If you think with tuples you lose the names of the out arguments there is a way to avoid this problem (but I don't know if in some situations out arguments are more efficient than tuples): pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int,"exp") frexp(real value); Also, with a tuple result there is no risk of confusion if an argument is "out" (do you remember the discussion where people have asked a ref/out annotation at Thank you for your answer, bye, bearophile
Mar 01 2012
bearophile <bearophileHUGS lycos.com> wrote: (snip)It's good for tuples to become more common in D code. Some time ago I have asked the built-in associative arrays to grow a method to iterate on key-value pairs, named "byPair": http://d.puremagic.com/issues/show_bug.cgi?id=5466(snip)Bye, bearophileAssociative arrays (should) have UFCS, so one could just define property auto byPair(AA)(AA aa) { return zip(aa.byKey, aa.byValue); } in std.array. Or object.di could just define a Pair!(K,V) structure which a Tuple!(K,V) has an opAssign defined for it.
Mar 01 2012
On 01.03.2012 23:08, bearophile wrote:I think std.typecons.Tuples merit to be a little more citizens in D and Phobos. I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple: pure nothrow trusted real frexp(real value, out int exp); ==> pure nothrow trusted Tuple!(real, int) frexp(real value); nothrow trusted real remquo(real x, real y, out int n); ==> nothrow trusted Tuple!(real, int) remquo(real x, real y);They're defined that way because they come from C, and they're in IEEE754.
Mar 02 2012