From slices to perfect imitators: opByValue

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Thu May 8 18:10:18 PDT 2014


On Thu, 08 May 2014 17:39:25 +0200
Sönke Ludwig via Digitalmars-d <digitalmars-d at puremagic.com> wrote:

> Am 08.05.2014 16:22, schrieb Jonathan M Davis via Digitalmars-d:
> > On Thu, 08 May 2014 14:48:18 +0200
> > Sönke Ludwig via Digitalmars-d <digitalmars-d at puremagic.com> wrote:
> >
> >> Am 08.05.2014 13:05, schrieb monarch_dodra:
> >>> On Thursday, 8 May 2014 at 07:09:24 UTC, Sönke Ludwig wrote:
> >>>> Just a general note: This is not only interesting for range/slice
> >>>> types, but for any user defined reference type (e.g. RefCounted!T
> >>>> or Isolated!T).
> >>>
> >>> Not necessarily: As soon as indirections come into play, you are
> >>> basically screwed, since const is "turtles all the way down".
> >>>
> >>> So for example, the conversion from "const RefCounted!T" to
> >>> "RefCounted!(const T)" is simply not possible, because it strips
> >>> the const-ness of the ref count.
> >>>
> >>> What we would *really* need here is NOT:
> >>> "const RefCounted!T" => "RefCounted!(const T)"
> >>> But rather
> >>> "RefCounted!T" => "RefCounted!(const T)"
> >>>
> >>> The idea is to cut out the "head const" directly. This also
> >>> applies to most ranges too BTW.
> >>>
> >>> We'd be much better of if we never used `const MyRange!T` to begin
> >>> with, but simply had a conversion from `MyRange!T` to
> >>> `MyRange!(const T)`, which references the same data.
> >>>
> >>> In fact, I'm wondering if this might not be a more interesting
> >>> direction to explore.
> >>
> >> The reference count _must_ be handled separate from the payload's
> >> const-ness, or a const(RefCount!T) would be completely
> >> dysfunctional.
> >
> > Unless the reference count is completely separate from
> > const(RefCount!T) (which would mean that the functions which
> > accessed the reference couldn't pure - otherwise they couldn't
> > access the reference count), const(RefCount!T) _is_ completely
> > dysfunctional. The fact that D's const can't be cast away in to do
> > mutation without violating the type system means that pretty much
> > anything involving references or pointers is dysfunctional with
> > const if you ever need to mutate any of it (e.g. for a mutex or a
> > reference count). std.typecons.RefCounted is completely broken if
> > you make it const, and I would expect that of pretty much any
> > wrapper object intended to do reference counting. Being able to do
> > RefCounted!T -> RefCounted!(const T) makes some sense, but
> > const(RefCounted!T) pretty much never makes sense.
> >
> > That being said, unlike monarch_dodra, I think that it's critical
> > that we find a way to do the equivalent of const(T[]) -> const(T)[]
> > with ranges - i.e. const(Range!T) or const(Range!(const T)) ->
> > Range!(const T). I don't think that Range!T -> Range!(const T) will
> > be enough at all. It's not necessarily the case that const(Range!T)
> > -> Range!(const T) would always work, but it's definitely the case
> > that it would work if the underlying data was in an array, and
> > given what it takes for a forward range to work, it might even be
> > the case that a forward range could do be made to do that
> > conversion by definition.
> >
> > The problem is the actual mechanism of converting const(Range!T) to
> > Range!(const T) in the first place (due to recursive template
> > instantiations and the fact that the compiler doesn't understand
> > that they're related). The concept itself is perfectly sound in
> > many cases - unlike with const(RefCounted!T) - because in most
> > cases, with a range, it's the data being referred to which needs to
> > stay const, whereas the bookkeeping stuff for it can be copied and
> > thus be made mutable. With a reference count, however, you have to
> > mutate what's actually being pointed to rather than being able to
> > make a copy and mutate that. So, const(RefCounted!T) ->
> > RefCounted!(const T) will never work - not unless the reference is
> > outside of const(RefCounted!T), in which case, it can't be pure,
> > which can be just as bad as not working with const.
> >
> > - Jonathan M Davis
> >
>
> Unless I'm completely mistaken, it's safe to cast away const when it
> is known that the original reference was constructed as mutable.
> Anyway, this is what I do in my own RefCount struct. But my main
> point was that any user defined reference type is affected by the
> head vs. tail const issue, not just range types. So a decent solution
> should solve it for all of those types.

Part of my point was that getting a tail-const slice of a range is
fundamentally different from trying to get a tail-const with a ref-counted
struct. With a range, the bookkeeping that would end up being mutable in the
tail-const slice can usually be copied in order to be mutable and still work
properly. In a ref-counted struct, however, the ref-count is a reference or
pointer and copying it wouldn't help. You need to be able to mutate the same
count that every other reference to the same object is using. And that
requires casting away const (thus violating the type system) rather than being
able to make a copy.

So, while it might be the case that the issue of tail-constness can be
generalized beyond slices in a useful way, and I don't think that it applies
to reference counting structs.

> BTW, since RefCount would usually do manual memory management, it
> can't be used in pure contexts anyway. Proper support of scope/lent
> pointers would solve that, though. Ideally, there would be a way to
> allow a RefCount!T to implicitly cast to T if passed as a scoped
> parameter.

Memory management shouldn't prevent pure from working. It definitely doesn't
with the GC, and while it might with malloc due to the fact that it's a C
function and not built-in, it should theoretically be possible to make it so
that malloc and can be used in a pure function. I'm not sure about free
though.

But ultimately, if you want a reference counting object to work with const or
immutable, it's going to need to keep the count separate from the object (e.g.
in a static AA) and thus won't work with pure.

- Jonathan M Davis


More information about the Digitalmars-d mailing list