Re: std.range.put vs R.put: Best practices?

2017-08-21 Thread Jon Degenhardt via Digitalmars-d-learn

On Monday, 21 August 2017 at 05:58:01 UTC, Jonathan M Davis wrote:
On Monday, August 21, 2017 02:34:23 Mike Parker via 
Digitalmars-d-learn wrote:
On Sunday, 20 August 2017 at 18:08:27 UTC, Jon Degenhardt 
wrote:

> Documentation for std.range.put
> (https://dlang.org/phobos/std_range_primitives.html#.put) has
>
> the intriguing line:
>> put should not be used "UFCS-style", e.g. r.put(e). Doing 
>> this
>> may call R.put directly, by-passing any transformation 
>> feature

>> provided by Range.put. put(r, e) is prefered.
>
> This raises the question of whether std.range.put is always 
> preferred over calling an output range's 'put' method, or if 
> there are times when calling an output range's 'put' method 
> directly is preferred. Also, it seems an easy oversight to 
> unintentionally call the wrong one.

>
> Does anyone have recommendations or best practice 
> suggestions for which form to use and when?


It's recommended to always use the utility function in 
std.range unless you are working with an output range that has 
a well known put implementation. The issue is that put can be 
implemented to take any number or type of arguments, but as 
long as it has an implementation with one parameter of the 
range's element type, then the utility function will do the 
right thing internally whether you pass multiple elements, a 
single element, an array... It's particularly useful in 
generic code where most ranges are used. But again, if you are 
working with a specific range type then you can do as you 
like. Also, when the output range is a dynamic array, UFCS 
with the utility function is fine.


As for mitigating the risk of calling the wrong one, when you 
do so you'll either get a compile-time error because of a 
parameter mismatch or it will do the right thing. If there's 
another likely outcome, I'm unaware of it.


To add to that, the free function put handles putting different 
character types to a range of characters (IIRC, it also handles 
putting entire strings as well), whereas a particular 
implementation of put probably doesn't. In principle, a 
specific range type could do everything that the free function 
does, but it's highly unlikely that it will.


In general, it's really just better to use the free function 
put, and arguably, we should have used a different function 
name for the output ranges themselves with the idea that the 
free function would always be the one called, and it would call 
the special function that the output ranges defined. 
Unfortunately, however, that's not how it works. In general, 
IMHO, output ranges really weren't thought out well enough. 
It's more like they were added as a countepart to input ranges 
because Andrei felt like they needed to be there rather than 
having them be fully fleshed out on their own. The result is a 
basic idea that's very powerful but that suffers in the details 
and probably needs at least a minor redesign (e.g. the output 
API has no concept of an output range that's full).


In any case, I'd just suggest that you never use put with UFCS. 
Unfortunately, if you're using UFCS enough, it becomes habit to 
just call the function as if it were a member function, which 
is then a problem when using output ranges, but we're kind of 
stuck at this point. On the bright side, it's really only 
likely to cause issues in generic code where the member 
function might work with your tests but not everything that's 
passed to it. In other cases, if what you're doing doesn't work 
with the member function, then the code won't compile, and 
you'll know to switch to using the free function.




Mike, Jonathan - Thanks for the detailed responses!

Yes, by habit I use UFCS, there is where potential for the wrong 
call comes from. I agree also that output ranges are very 
powerful in concept, but the details are not fully fleshed out at 
this point. A few enhancements could make it much more compelling.


--Jon


Re: std.range.put vs R.put: Best practices?

2017-08-21 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, August 21, 2017 02:34:23 Mike Parker via Digitalmars-d-learn 
wrote:
> On Sunday, 20 August 2017 at 18:08:27 UTC, Jon Degenhardt wrote:
> > Documentation for std.range.put
> > (https://dlang.org/phobos/std_range_primitives.html#.put) has
> >
> > the intriguing line:
> >> put should not be used "UFCS-style", e.g. r.put(e). Doing this
> >> may call R.put directly, by-passing any transformation feature
> >> provided by Range.put. put(r, e) is prefered.
> >
> > This raises the question of whether std.range.put is always
> > preferred over calling an output range's 'put' method, or if
> > there are times when calling an output range's 'put' method
> > directly is preferred. Also, it seems an easy oversight to
> > unintentionally call the wrong one.
> >
> > Does anyone have recommendations or best practice suggestions
> > for which form to use and when?
> >
> > --Jon
>
> It's recommended to always use the utility function in std.range
> unless you are working with an output range that has a well known
> put implementation. The issue is that put can be implemented to
> take any number or type of arguments, but as long as it has an
> implementation with one parameter of the range's element type,
> then the utility function will do the right thing internally
> whether you pass multiple elements, a single element, an array...
> It's particularly useful in generic code where most ranges are
> used. But again, if you are working with a specific range type
> then you can do as you like. Also, when the output range is a
> dynamic array, UFCS with the utility function is fine.
>
> As for mitigating the risk of calling the wrong one, when you do
> so you'll either get a compile-time error because of a parameter
> mismatch or it will do the right thing. If there's another likely
> outcome, I'm unaware of it.

To add to that, the free function put handles putting different character
types to a range of characters (IIRC, it also handles putting entire strings
as well), whereas a particular implementation of put probably doesn't. In
principle, a specific range type could do everything that the free function
does, but it's highly unlikely that it will.

In general, it's really just better to use the free function put, and
arguably, we should have used a different function name for the output
ranges themselves with the idea that the free function would always be the
one called, and it would call the special function that the output ranges
defined. Unfortunately, however, that's not how it works. In general, IMHO,
output ranges really weren't thought out well enough. It's more like they
were added as a countepart to input ranges because Andrei felt like they
needed to be there rather than having them be fully fleshed out on their
own. The result is a basic idea that's very powerful but that suffers in the
details and probably needs at least a minor redesign (e.g. the output API
has no concept of an output range that's full).

In any case, I'd just suggest that you never use put with UFCS.
Unfortunately, if you're using UFCS enough, it becomes habit to just call
the function as if it were a member function, which is then a problem when
using output ranges, but we're kind of stuck at this point. On the bright
side, it's really only likely to cause issues in generic code where the
member function might work with your tests but not everything that's passed
to it. In other cases, if what you're doing doesn't work with the member
function, then the code won't compile, and you'll know to switch to using
the free function.

- Jonathan M Davis



Re: std.range.put vs R.put: Best practices?

2017-08-20 Thread Mike Parker via Digitalmars-d-learn

On Sunday, 20 August 2017 at 18:08:27 UTC, Jon Degenhardt wrote:
Documentation for std.range.put 
(https://dlang.org/phobos/std_range_primitives.html#.put) has 
the intriguing line:


put should not be used "UFCS-style", e.g. r.put(e). Doing this 
may call R.put directly, by-passing any transformation feature 
provided by Range.put. put(r, e) is prefered.


This raises the question of whether std.range.put is always 
preferred over calling an output range's 'put' method, or if 
there are times when calling an output range's 'put' method 
directly is preferred. Also, it seems an easy oversight to 
unintentionally call the wrong one.


Does anyone have recommendations or best practice suggestions 
for which form to use and when?


--Jon


It's recommended to always use the utility function in std.range 
unless you are working with an output range that has a well known 
put implementation. The issue is that put can be implemented to 
take any number or type of arguments, but as long as it has an 
implementation with one parameter of the range's element type, 
then the utility function will do the right thing internally 
whether you pass multiple elements, a single element, an array... 
It's particularly useful in generic code where most ranges are 
used. But again, if you are working with a specific range type 
then you can do as you like. Also, when the output range is a 
dynamic array, UFCS with the utility function is fine.


As for mitigating the risk of calling the wrong one, when you do 
so you'll either get a compile-time error because of a parameter 
mismatch or it will do the right thing. If there's another likely 
outcome, I'm unaware of it.


std.range.put vs R.put: Best practices?

2017-08-20 Thread Jon Degenhardt via Digitalmars-d-learn
Documentation for std.range.put 
(https://dlang.org/phobos/std_range_primitives.html#.put) has the 
intriguing line:


put should not be used "UFCS-style", e.g. r.put(e). Doing this 
may call R.put directly, by-passing any transformation feature 
provided by Range.put. put(r, e) is prefered.


This raises the question of whether std.range.put is always 
preferred over calling an output range's 'put' method, or if 
there are times when calling an output range's 'put' method 
directly is preferred. Also, it seems an easy oversight to 
unintentionally call the wrong one.


Does anyone have recommendations or best practice suggestions for 
which form to use and when?


--Jon