www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.range: Order of arguments unluckily chosen?

reply Timon Gehr <timon.gehr gmx.ch> writes:
From my code:

writeln(reduce!"a+b"(stride(take(recurrence!"a[n-2]+a[n-1]"(2,3),31),3)));
(This solves project euler problem 3);

I think that should look more like:

writeln(reduce!"a+b"(stride(3,take(31,recurrence!"a[n-2]+a[n-1]"(2,3)))));

rationale:
The second version is WAY easier to read, all the information needed is there
without having to jump around in the code:

1st version translated to English:
Sum up every j-th element of the first i elements of fib, which is computed by
the recurrence a[n]=a[n-2]+a[n-1] with a[0]=2 and a[1]=3, where i=31 and j=3.

2nd version translated to English:
Sum up every 3th element of the first 31 elements of fib, which is computed by
the recurrence a[n]=a[n-2]+a[n-1] with a[0]=2 and a[1]=3.

1st version English is even better than the code, because you can search the
text for 'i' or 'j'. In the code you have to count parentheses.

Yes, changing the order would break UFCS. But UFCS works with arrays only, all
the other use cases are broken. (that are most cases)
Also, there is a function takeOne. That is more similar to take(1,...) than
take(...,1). The amount of elements taken should be closer to the name of the
action, because it is needed to fully describe it. (take!31(fib) would be even
closer, but that does not work with runtime values.)

I think the order should be swapped. It is also the way functional language
libraries handle it. It works better with currying too.

Any comments/arguments on why the current order is the right order?


Timon
Jun 04 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I'd kill for some UFCS action. Here's my interpretation (only the
first one works of course):

    range = take(stride(cycle([1, 2, 3, 4]), 2), 2);  // :(

    range = take(stride(2, cycle(2, [1, 2, 3, 4])));  // :)

    range = cycle([1, 2, 3, 4]).stride(2).take(2);    // :D
Jun 04 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Oh and I managed to screw up the second one, which should be:

range = take(2, stride(2, cycle([1, 2, 3, 4])));  // :)

All the more reason to have UFCS.
Jun 04 2011
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Timon Gehr:

 It is also the way functional language
 libraries handle it. It works better with currying too.

This is quite true. The order of all Haskell Prelude functions are designed to allow the most handy usage of currying: Prelude> let a = [0..10] Prelude> a [0,1,2,3,4,5,6,7,8,9,10] Prelude> take5 = take 5 Prelude> take5 a [0,1,2,3,4] If you program in Haskell you find that it's actually more handy to have the number of items of take as first argument. -------------------- Andrej Mitrovic:
 I'd kill for some UFCS action. Here's my interpretation (only the
 first one works of course):
     range = take(stride(cycle([1, 2, 3, 4]), 2), 2);  // :(
     range = take(stride(2, cycle(2, [1, 2, 3, 4])));  // :)
     range = cycle([1, 2, 3, 4]).stride(2).take(2);    // :D

Haskell solves this problem with two really handy operators, "." and "$", example: stride _ [] = [] stride n (x:xs) = x : stride n (drop (n-1) xs) main = print $ take 2 $ cycle $ stride 2 [1, 2, 3, 4] Bye, bearophile
Jun 04 2011
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 06/04/2011 03:11 PM, Timon Gehr wrote:
  From my code:

 writeln(reduce!"a+b"(stride(take(recurrence!"a[n-2]+a[n-1]"(2,3),31),3)));
 (This solves project euler problem 3);

 I think that should look more like:

 writeln(reduce!"a+b"(stride(3,take(31,recurrence!"a[n-2]+a[n-1]"(2,3)))));
 rationale:
 The second version is WAY easier to read, all the information needed is there
 without having to jump around in the code:

 1st version translated to English:
 Sum up every j-th element of the first i elements of fib, which is computed by
 the recurrence a[n]=a[n-2]+a[n-1] with a[0]=2 and a[1]=3, where i=31 and j=3.

 2nd version translated to English:
 Sum up every 3th element of the first 31 elements of fib, which is computed by
 the recurrence a[n]=a[n-2]+a[n-1] with a[0]=2 and a[1]=3.

 1st version English is even better than the code, because you can search the
 text for 'i' or 'j'. In the code you have to count parentheses.

 Yes, changing the order would break UFCS. But UFCS works with arrays only, all
 the other use cases are broken. (that are most cases)
 Also, there is a function takeOne. That is more similar to take(1,...) than
 take(...,1). The amount of elements taken should be closer to the name of the
 action, because it is needed to fully describe it. (take!31(fib) would be even
 closer, but that does not work with runtime values.)

 I think the order should be swapped. It is also the way functional language
 libraries handle it. It works better with currying too.

 Any comments/arguments on why the current order is the right order?

Well this hurts. With the same in mind as you, I initially defined take to take the number first. But OO people wanted to write array.take(3), so I changed the order. Don't forget that UFCS is still on the table. Impossible to please everybody! I don't know what to do to improve the situation. Andrei
Jun 04 2011
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-06-04 18:55:54 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 On 06/04/2011 03:11 PM, Timon Gehr wrote:
 I think the order should be swapped. It is also the way functional language
 libraries handle it. It works better with currying too.
 
 Any comments/arguments on why the current order is the right order?

Well this hurts. With the same in mind as you, I initially defined take to take the number first. But OO people wanted to write array.take(3), so I changed the order. Don't forget that UFCS is still on the table. Impossible to please everybody! I don't know what to do to improve the situation.

Perhaps like this: auto take(R)(uint count, R this) { ... } Note that the second parameters's name is "this", which would mean that the second argument is the one that disappear using the member syntax. It can then be called this way: take(3, range); or this way: range.take(3); Having to specify explicitly whether a function is meant for member-syntax this way would also fix a couple of issues regarding UFCS: UFCS properties would be able to work correctly, and name clashes would happen less often when using the member syntax. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jun 04 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
Michel Fortin wrote:
 Perhaps like this:

  auto take(R)(uint count, R this) { ... }

 Note that the second parameters's name is "this", which would mean that
 the second argument is the one that disappear using the member syntax.

 It can then be called this way:

  take(3, range);

 or this way:

  range.take(3);

 Having to specify explicitly whether a function is meant for
 member-syntax this way would also fix a couple of issues regarding
 UFCS: UFCS properties would be able to work correctly, and name clashes
 would happen less often when using the member syntax.


 --
 Michel Fortin
 michel.fortin michelf.com
 http://michelf.com/

I was about to propose the same. It also nicely disallows cases where UFCS is actually quite nonsensical, like Eg. y.atan2(x); But since it requires a language change, we'll be waiting a long time for it. (Even though everyone on this newsgroup seems to be talking about UFCS a lot.) How would the function make use of the parameter? I suggest that an explicit "this" should be required. (making it just a specially named parameter) Also, it would have to be discussed if structures and classes can define member functions using this syntax. I suggest to disallow it for non-static methods. Note: C# has a feature called "extension methods", that does basically the same in C#, "this" is just a parameter type modifier. But it is less powerful than the proposal, because it can be applied to the first parameter only. I am a bit concerned about this though: int foo(int a, int this, int c); foo(1,2,3); <=> (2).foo(1,3); // somewhat strange Is there any use case for that? Timon
Jun 04 2011
prev sibling next sibling parent Mehrdad <wfunction hotmail.com> writes:
On 6/4/2011 4:25 PM, Michel Fortin wrote:
 Perhaps like this:

     auto take(R)(uint count, R this) { ... }

 Note that the second parameters's name is "this", which would mean 
 that the second argument is the one that disappear using the member 
 syntax.

 It can then be called this way:

     take(3, range);

 or this way:

     range.take(3);

 Having to specify explicitly whether a function is meant for 
 member-syntax this way would also fix a couple of issues regarding 
 UFCS: UFCS properties would be able to work correctly, and name 
 clashes would happen less often when using the member syntax.

+1 for the idea, it's pretty similar to C#'s extension method syntax, and it's pretty intuitive IMHO.
Jun 04 2011
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
so wrote:
 Nice idea to make sense out of the UFCS, but i fail to realize the
 actually need for UFCS to begin with.
 To me, the need to convert "take(3, range)" to "range.take(3)" is
 non-existent (and this wouldn't be the only reason i could come up with),
 probably i am missing something. And because everyone likes this, it must
 be something big :)

I think it is about readability of nested statements: take(10,stride(2,cycle([3,2,5,3]))); vs. [3,2,5,3].cycle().stride(2).take(10); The first one is: "Take 10 of every 2nd element of cyclic [3,2,5,3,...]. The second one is: "Start with [3,2,5,3]. Then cycle that. Then only look at every 2nd element of that. Finally, take 10 elements out of the resulting range." The second version writes the actions in the order they are performed, while the functional way is more like what you'd get if you had to describe the entire process in a single sentence. It also reduces nesting of parentheses. I am fine with both. But I dislike take(stride(cycle([3,2,5,3]),2),10); Timon
Jun 06 2011
prev sibling next sibling parent so <so so.so> writes:
 Well this hurts. With the same in mind as you, I initially defined take  
 to take the number first. But OO people wanted to write array.take(3),  
 so I changed the order. Don't forget that UFCS is still on the table.

 Impossible to please everybody!

 I don't know what to do to improve the situation.

This is the reason of quite many of the discussions here boils down to, we (not being a contributor, maybe i should say "you") shouldn't cater for particular groups be it either Functional or OO. If they don't feel something right, they should also consider the other side of the table, and come up with compelling arguments. Reading recent posts, i really started to think the word "subjectivity" is just a horse-waste to divert our attention. Given none of our backgrounds match one another, none of our experiences match with one another, none of our open/close mindedness match with one another. I like OO more than most of the people here, but it is simply a freaking paradigm. If some people just want everything OO way, knowing none of its drawbacks... Forgive my language, fuck them. We have many languages out there just doing that, and yet the only reason we are here is that we know they suck.
Jun 06 2011
prev sibling next sibling parent so <so so.so> writes:
On Sun, 05 Jun 2011 02:25:46 +0300, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2011-06-04 18:55:54 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> said:

 On 06/04/2011 03:11 PM, Timon Gehr wrote:
 I think the order should be swapped. It is also the way functional  
 language
 libraries handle it. It works better with currying too.
  Any comments/arguments on why the current order is the right order?

take to take the number first. But OO people wanted to write array.take(3), so I changed the order. Don't forget that UFCS is still on the table. Impossible to please everybody! I don't know what to do to improve the situation.

Perhaps like this: auto take(R)(uint count, R this) { ... } Note that the second parameters's name is "this", which would mean that the second argument is the one that disappear using the member syntax. It can then be called this way: take(3, range); or this way: range.take(3); Having to specify explicitly whether a function is meant for member-syntax this way would also fix a couple of issues regarding UFCS: UFCS properties would be able to work correctly, and name clashes would happen less often when using the member syntax.

Nice idea to make sense out of the UFCS, but i fail to realize the actually need for UFCS to begin with. To me, the need to convert "take(3, range)" to "range.take(3)" is non-existent (and this wouldn't be the only reason i could come up with), probably i am missing something. And because everyone likes this, it must be something big :)
Jun 06 2011
prev sibling next sibling parent KennyTM~ <kennytm gmail.com> writes:
On Jun 5, 11 06:55, Andrei Alexandrescu wrote:
 Well this hurts. With the same in mind as you, I initially defined take
 to take the number first. But OO people wanted to write array.take(3),
 so I changed the order. Don't forget that UFCS is still on the table.

 Impossible to please everybody!

 I don't know what to do to improve the situation.


 Andrei

You could provide an overload? Take!R take(R)(size_t n, R input) if (!is(Unqual!R : size_t)) { return take(input, n); } Like PHP's implode() ;).
Jun 06 2011
prev sibling parent so <so so.so> writes:
 I think it is about readability of nested statements:

 take(10,stride(2,cycle([3,2,5,3])));

 vs.

 [3,2,5,3].cycle().stride(2).take(10);

 The first one is: "Take 10 of every 2nd element of cyclic [3,2,5,3,...].
 The second one is: "Start with [3,2,5,3]. Then cycle that. Then only  
 look at every
 2nd element of that. Finally, take 10 elements out of the resulting  
 range."

Thanks, this alone makes me realize the need for UFCS. Until now i was thinking it is just about taste, look, that some people like the OO way.
 The second version writes the actions in the order they are performed,  
 while the
 functional way is more like what you'd get if you had to describe the  
 entire
 process in a single sentence. It also reduces nesting of parentheses. I  
 am fine
 with both. But I dislike

 take(stride(cycle([3,2,5,3]),2),10);

I agree.
Jun 06 2011