March 02, 2012
On 02.03.2012 6:06, 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.

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'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.
>

+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.
>
> I think that the tuple unpacking syntax is able to avoid part of your problems.

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 Olshansky
March 02, 2012
On Friday, March 02, 2012 11:41:16 Dmitry Olshansky wrote:
> On 02.03.2012 6:06, 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.
> 
> just stick in .expand ?
> 
> void f(int x, int y){ }
> 
> void main()
> {
> 	Tuple!(int, int) a;
> 	f(a.expand);
> }

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 Davis
March 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.
March 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 Davis

How 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]);
March 02, 2012
Le 02/03/2012 02:10, Jonathan M Davis a écrit :
> On Thursday, March 01, 2012 18:57:15 bearophile wrote:
>> Jonathan M Davis:
>>> They also often do poorly when you
>>> need to use the functional programming,
>>
>> As you know functional languages use tuples all the time.
>
> 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.
>
>>> 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.
>
> 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 Davis

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.
March 02, 2012
Le 02/03/2012 03:06, bearophile a écrit :
> 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);
>

You got it right. +1
March 02, 2012
On Friday, March 02, 2012 09:31:14 kennytm wrote:
> 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 Davis
> 
> How 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]);

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 Davis
March 02, 2012
On Friday, March 02, 2012 10:27:02 deadalnix wrote:
> 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.

Yes. Functional languages use tuples. But I'm talking about chaining functions like you would in a functional language. But functional languages use pattern matching and other stuff to make it not as big a problem as it is in D, and often in functional languages, you _still_ have to do their equivalent of assigning the pieces of the tuple to variables. Tuples are a bane of function chaining.

- Jonathan M Davis
March 02, 2012
Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> On Friday, March 02, 2012 09:31:14 kennytm wrote:
>> 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 Davis
>> 
>> How 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]);
> 
> 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 Davis

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'?
March 02, 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.