www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - UFCS for arguments other than first?

reply Heisenberg <nixiwevoke rootfest.net> writes:
What would it take to implement the Uniform Function Call Syntax 
for a function's argument which is not the first?

Right now it is possible to do the following:

 int someNumber(int a, int b)
 {
     return a + b;
 }

 void main()
 {
     int n1 = 5;
     int n2 = n1.someNumber(n1 + 1); // Here the `n1` is `a`
 }
But it's not possible to use the `n1' as `b`, for UFCS only substitutes the expression before the `.` for the first argument. This limits a little bit the flexibility of UFCS. What would it take to specify the argument in which UFCS is going to put the expression before the `.`? This would make it even more useful. For instance:
 string someStr = "Yohoho!";
 // To format and print it, one could use `printf':
 writefln("The string is \"%s\".", someStr);
 // But since it is not in the first place,
 // it is not possible to use UFCS for it:
 someStr.writefln("The string is \"%s\"."); // ^ Prints `Yohoho!'
 // One could use tuples to pass
 // the expanded list of strings:
 tuple("The string is \"%s\".", someStr)[].writefln();
 // But it would be much easier if there was
 // some way to tell the compiler
 // to position the argument,
 // for example:
 someStr&1.writefln("The string is \"%s\".");
 //     ^            ^ argument at the position [0]
 //     ^ argument to be put at the position [1]
 // This could even allow to insert the argument
 // at more than one position at once:
 someStr&1&3&4.writefln("Say it, lass!\n" ~
                        "%s\nI'm a pirate!\n" ~  // someStr goes 
 here (1)
                        "%s\nHe's a pirate!\n" ~ // not here (2)
                        "She's a pirate!\n%s" ~  // here (3)
                        "Ale! Rum! %s\n" ~       // and here (4)
                        "Wait.. Why is the rum gone?",
                        "You are a pirate!");    // gets shifted
                                                 // to position 
 (2)
Nov 11 2016
next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Friday, 11 November 2016 at 16:39:26 UTC, Heisenberg wrote:
 What would it take to implement the Uniform Function Call 
 Syntax for a function's argument which is not the first?

 Right now it is possible to do the following:

 int someNumber(int a, int b)
 {
     return a + b;
 }

 void main()
 {
     int n1 = 5;
     int n2 = n1.someNumber(n1 + 1); // Here the `n1` is `a`
 }
But it's not possible to use the `n1' as `b`, for UFCS only substitutes the expression before the `.` for the first argument. This limits a little bit the flexibility of UFCS. What would it take to specify the argument in which UFCS is going to put the expression before the `.`? This would make it even more useful. For instance:
 string someStr = "Yohoho!";
 // To format and print it, one could use `printf':
 writefln("The string is \"%s\".", someStr);
 // But since it is not in the first place,
 // it is not possible to use UFCS for it:
 someStr.writefln("The string is \"%s\"."); // ^ Prints 
 `Yohoho!'
 // One could use tuples to pass
 // the expanded list of strings:
 tuple("The string is \"%s\".", someStr)[].writefln();
 // But it would be much easier if there was
 // some way to tell the compiler
 // to position the argument,
 // for example:
 someStr&1.writefln("The string is \"%s\".");
 //     ^            ^ argument at the position [0]
 //     ^ argument to be put at the position [1]
 // This could even allow to insert the argument
 // at more than one position at once:
 someStr&1&3&4.writefln("Say it, lass!\n" ~
                        "%s\nI'm a pirate!\n" ~  // someStr 
 goes here (1)
                        "%s\nHe's a pirate!\n" ~ // not here (2)
                        "She's a pirate!\n%s" ~  // here (3)
                        "Ale! Rum! %s\n" ~       // and here (4)
                        "Wait.. Why is the rum gone?",
                        "You are a pirate!");    // gets shifted
                                                 // to position 
 (2)
I'm sure there are loads of corner cases this doesn't cover, but: template inPos(uint n, alias f) { auto inPos(Args...)(auto ref Args args) { return f(args[1 .. n+1], args[0], args[n+1 .. $]); } } import std.stdio; alias formatSecond = inPos!(1, writefln); void main() { 1.writeln(2,3,4,5); 1.inPos!(2, writeln)(2,3,4,5); 42.formatSecond("The answer is %d"); } and of course you could easily develop more powerful templates that could do any other order manipulations you like.
Nov 11 2016
parent reply Heisenberg <nixiwevoke rootfest.net> writes:
On Friday, 11 November 2016 at 17:07:50 UTC, John Colvin wrote:
 I'm sure there are loads of corner cases this doesn't cover, 
 but:

 template inPos(uint n, alias f)
 {
     auto inPos(Args...)(auto ref Args args)
     {
         return f(args[1 .. n+1], args[0], args[n+1 .. $]);
     }
 }

 import std.stdio;
 alias formatSecond = inPos!(1, writefln);

 void main()
 {

     1.writeln(2,3,4,5);
     1.inPos!(2, writeln)(2,3,4,5);

     42.formatSecond("The answer is %d");
 }

 and of course you could easily develop more powerful templates 
 that could do any other order manipulations you like.
That might be a solution as well. Specifying the position near the variable will make the code much more straightforward to read though, while not forcing the programmer to declare separate templates in order to specify the position of a function's argument..
Nov 11 2016
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Friday, 11 November 2016 at 18:33:09 UTC, Heisenberg wrote:
 On Friday, 11 November 2016 at 17:07:50 UTC, John Colvin wrote:
 I'm sure there are loads of corner cases this doesn't cover, 
 but:

 template inPos(uint n, alias f)
 {
     auto inPos(Args...)(auto ref Args args)
     {
         return f(args[1 .. n+1], args[0], args[n+1 .. $]);
     }
 }

 import std.stdio;
 alias formatSecond = inPos!(1, writefln);

 void main()
 {

     1.writeln(2,3,4,5);
     1.inPos!(2, writeln)(2,3,4,5);

     42.formatSecond("The answer is %d");
 }

 and of course you could easily develop more powerful templates 
 that could do any other order manipulations you like.
That might be a solution as well. Specifying the position near the variable will make the code much more straightforward to read though, while not forcing the programmer to declare separate templates in order to specify the position of a function's argument..
Not sure if either of those reasons are language-change worthy.
Nov 11 2016
parent reply Heisenberg <nixiwevoke rootfest.net> writes:
On Friday, 11 November 2016 at 21:51:29 UTC, John Colvin wrote:
 On Friday, 11 November 2016 at 18:33:09 UTC, Heisenberg wrote:
 On Friday, 11 November 2016 at 17:07:50 UTC, John Colvin wrote:
 I'm sure there are loads of corner cases this doesn't cover, 
 but:

 template inPos(uint n, alias f)
 {
     auto inPos(Args...)(auto ref Args args)
     {
         return f(args[1 .. n+1], args[0], args[n+1 .. $]);
     }
 }

 import std.stdio;
 alias formatSecond = inPos!(1, writefln);

 void main()
 {

     1.writeln(2,3,4,5);
     1.inPos!(2, writeln)(2,3,4,5);

     42.formatSecond("The answer is %d");
 }

 and of course you could easily develop more powerful 
 templates that could do any other order manipulations you 
 like.
That might be a solution as well. Specifying the position near the variable will make the code much more straightforward to read though, while not forcing the programmer to declare separate templates in order to specify the position of a function's argument..
Not sure if either of those reasons are language-change worthy.
Isn't the whole point of UFCS implementation in providing better re-usability and scalability, helping the encapsulation, making the chaining of function calls easier? - http://www.drdobbs.com/cpp/uniform-function-call-syntax/232700394 - https://forum.dlang.org/reply/ldlgoxuivwinqimtatqq forum.dlang.org
Nov 11 2016
parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Friday, November 11, 2016 22:48:24 Heisenberg via Digitalmars-d wrote:
 Isn't the whole point of UFCS implementation in providing better
 re-usability and scalability, helping the encapsulation, making
 the chaining of function calls easier?
The primary benefit of UFCS is so that generic code can call a function without caring whether it's dealing with a member function or a free function, allowing you to declare free functions to act like member functions for types that are lacking the member function in question or allowing a member function to provide a specialization of a more general free function in cases where the type could implement the function more efficiently than the general implementation would be (e.g. a sorted, random-access range could implement find itself to do binary search, or if a range were backed by a binary tree, it could implement find to do a lookup rather than searching linearly). The secondary benefit that leads many folks to want to use UFCS is that they find it easier to read code when the functions are chained with . rather than by nesting function calls in the normal manner, but that's a matter of preference rather than providing any actual technical benefit. If the function is a free function, then it really doesn't matter whether you use UFCS or not with regards to what the code does or your ability to chain function calls. It's just personal preference based on what you find easier or harder to read. As for reusability and scalability, I don't see how UFCS has any impact on those at all. Making a function be a generic free function can help with that, but that doesn't require that you use UFCS. Similarly, if you want to encapsulate code by using free functions rather than member functions, UFCS isn't required, and if all you're doing is turning a member function into a free function that takes that type (as opposed to making the function generic), then it doesn't even improve encapsulation unless its in a different module, because everything inside of a module has access to everything else within a module. Ultimately, the only technical benefit from UFCS is that it allows you to call a function without caring whether it's a member function or a free function, which is of great benefit to generic code and not really much else. Otherwise, it's just a syntactic preference - albeit one that many people prefer. - Jonathan M Davis
Nov 11 2016
parent Heisenberg <nixiwevoke rootfest.net> writes:
On Friday, 11 November 2016 at 23:27:30 UTC, Jonathan M Davis 
wrote:
 Ultimately, the only technical benefit from UFCS is that it 
 allows you to call a function without caring whether it's a 
 member function or a free function, which is of great benefit 
 to generic code and not really much else. Otherwise, it's just 
 a syntactic preference - albeit one that many people prefer.

 - Jonathan M Davis
If many people prefer it - why not develop it further? UCFS, along with other 'D Gems' is one of the selling points of the language, after all.
Nov 12 2016
prev sibling parent reply Temtaime <temtaime gmail.com> writes:
On Friday, 11 November 2016 at 16:39:26 UTC, Heisenberg wrote:
 What would it take to implement the Uniform Function Call 
 Syntax for a function's argument which is not the first?

 Right now it is possible to do the following:

 int someNumber(int a, int b)
 {
     return a + b;
 }

 void main()
 {
     int n1 = 5;
     int n2 = n1.someNumber(n1 + 1); // Here the `n1` is `a`
 }
But it's not possible to use the `n1' as `b`, for UFCS only substitutes the expression before the `.` for the first argument. This limits a little bit the flexibility of UFCS. What would it take to specify the argument in which UFCS is going to put the expression before the `.`? This would make it even more useful. For instance:
 string someStr = "Yohoho!";
 // To format and print it, one could use `printf':
 writefln("The string is \"%s\".", someStr);
 // But since it is not in the first place,
 // it is not possible to use UFCS for it:
 someStr.writefln("The string is \"%s\"."); // ^ Prints 
 `Yohoho!'
 // One could use tuples to pass
 // the expanded list of strings:
 tuple("The string is \"%s\".", someStr)[].writefln();
 // But it would be much easier if there was
 // some way to tell the compiler
 // to position the argument,
 // for example:
 someStr&1.writefln("The string is \"%s\".");
 //     ^            ^ argument at the position [0]
 //     ^ argument to be put at the position [1]
 // This could even allow to insert the argument
 // at more than one position at once:
 someStr&1&3&4.writefln("Say it, lass!\n" ~
                        "%s\nI'm a pirate!\n" ~  // someStr 
 goes here (1)
                        "%s\nHe's a pirate!\n" ~ // not here (2)
                        "She's a pirate!\n%s" ~  // here (3)
                        "Ale! Rum! %s\n" ~       // and here (4)
                        "Wait.. Why is the rum gone?",
                        "You are a pirate!");    // gets shifted
                                                 // to position 
 (2)
You are wrong. We can use n1 for the second parameter. int someNumber(int a, int b) { return a + b; } int main() { int n1 = 5; return (n1.someNumber = n1); // 10 }
Nov 11 2016
parent Heisenberg <nixiwevoke rootfest.net> writes:
On Saturday, 12 November 2016 at 06:42:24 UTC, Temtaime wrote:
 You are wrong. We can use n1 for the second parameter.

 int someNumber(int a, int b)
  {
      return a + b;
  }

 int main()
 {
      int n1 = 5;
      return (n1.someNumber = n1); // 10
  }
In this specific case, yes - however:
int getResult(int a, int b, int c)
{
     return(a * 2 - b * 3 + c * 4);
}
int main()
{
    int n1 = 3, n2 = 5, n3 = 7;
    // If one needs to chain n3,
    // but requires it to be entered
    // as the third argument?
    return(n3.getResult(n1, n2)); // `n3` is the `a`
    return(n3.getResult = tuple(n1, n2)[]); // still `a`
}
Being able to specify the position would help maintain 'the chain' intact.
Nov 12 2016