Should we add drop to Phobos?
Jonathan M Davis
jmdavisProg at gmx.com
Tue Aug 16 14:41:29 PDT 2011
On Tuesday, August 16, 2011 13:32 Dmitry Olshansky wrote:
> On 16.08.2011 23:37, Jonathan M Davis wrote:
> > On Tuesday, August 16, 2011 14:27:18 Dmitry Olshansky wrote:
> >> On 16.08.2011 6:33, Jonathan M Davis wrote:
> >>> On Tuesday, August 16, 2011 04:26:48 Martin Nowak wrote:
> >>>> I personally like immutable methods way better than a ref changing
> >>>> function like popFrontN.
> >>>> As you pointed out writing str.find(";").drop(2).find(";").drop(2) is
> >>>> cleaner than writing this with popFrontN.
> >>>>
> >>>> Drop will have issues for input ranges.
> >>>
> >>> No more than many range-based functions do. Yes, it'll alter the input,
> >>> that's to be expected with any range-based function that you pass an
> >>> input range to. drop could be made to take only a forward range, but
> >>> that seems unnecessarily limiting. You just have to be aware of the
> >>> fact that range-based functions always remove elements from input
> >>> ranges when they process them - either that or they don't work with
> >>> input ranges.
> >>>
> >>>> Adding a ref count parameter overload to let you know how many
> >>>> elements
> >>>> were dropped/not dropped seems too messy. No idea for that one.
> >>>
> >>> It's a tradeoff. And if you really want drop, you probably don't care
> >>> anyway, since what you'd be doing would be chaining functions. If you
> >>> really care, just use popFrontN.
> >>>
> >>>> The documentation should clearly state that this offers similar
> >>>> functionality to popFrontN but it's purpose
> >>>> is to enable a different syntax so that people don't get completely
> >>>> confused.
> >>>
> >>> It does. This is the ddoc comment that I have for it:
> >>>
> >>> /++
> >>>
> >>> Pops $(D n) elements off of the given range and returns it. If
> >>> the length of the given range is less than $(D n), then an
> >>> empty range is returned. The original range is unaltered as
> >>> long as it's a value type.
> >>
> >> Shouldn't this be : "as long as it's not an Input range", for everything
> >> else there is this clanky .save()
> >
> > No, I meant what it says. drop doesn't call save, so it'll work with
> > input ranges. However, if you use a range which is a class with it, then
> > it's going to alter the original range.
>
> I was arguing that Input range is always altered no matter if it's
> struct or class or whatnot. So if it's used on forward range it should
> really call .save (worth a single static if ?) I think, thus avoiding
> side effects even if it's class.
> For input ranges it becomes equivalent of (r.popFrontN(n), r); with all
> side effects as you described.
> Seriously, "value type input ranges" are forward ranges, isn't it? (at
> least they should be)
There's a definite argument for static if-ing forward ranges like that. And
perhaps we should do that in general in Phobos (certainly, I don't think that
we always handle reference-type ranges properly right now, and the best way to
handle that is up for some debate). But the point was that if you pass a class
which is a forward range, it's not going to be saved, whereas a struct which
was a value type would be.
In general, the problem is a bit hairy, given that structs are usually value
types (but not always) and classes never are - and most of the time we're
dealing with ranges that are either structs or arrays - most of which are
value types in the sense that if you pass them to a function without ref,
altering them won't alter the original (though it can generally alter the
elements in the range and thus affect the original). And very few ranges that
we deal with aren't forward ranges. So, I'm pretty sure that we're not
handling all of these cases correctly in Phobos. It's been bothering me of
late, and I've been debating how we should best be going about it.
I think that in the general case, right now, if you pass a class range to a
function without calling save, it's going to alter the original, and I suspect
that there are a few places that we assume that a range isn't going to be
altered by a function we pass it to (since it usually doesn't as we're usually
using forward ranges) and that there's code which is supposed to work with
input ranges, but actually doesn't. So, it's on my todo list to create tests
for std.range and std.algorithm which make sure that functions handle input
ranges and class ranges properly.
That still leaves the question open as to how best handle cases where a
forward range which is a struct is automatically saved (since it's copied when
it's passed to a function) but where a class isn't automatically saved (since
it's a reference type). We should probably just make it so that forward ranges
always have save called on them at the top of a range-based function, even if
the function works with input ranges - then it would be consistent across all
types of forward ranges. At least, that's the best that I can think of, so I
should probably just change drop to work that way, which would make for a
cleaner explanation in the documentation, if nothing else.
However, I do think that this is a problem that needs to be addressed and
tested for in Phobos in general. We're too used to how arrays and structs
wrapped around arrays function as ranges and not enough used to dealing with
other types of ranges which have reference semantics.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list