On Saturday, 15 May 2021 at 11:51:11 UTC, Adam D. Ruppe wrote:
On Saturday, 15 May 2021 at 11:25:10 UTC, Chris Piker wrote:
The idea is you aren't supposed to care what the type is, just
what attributes it has, e.g., can be indexed, or can be
assigned, etc.
(Warning, new user rant ahead. Eye rolling warranted and
encouraged)
I'm trying to do that, but range3 and range2 are written by me
not a Phobos wizard, and there's a whole library of template
functions a person needs to learn to make their own pipelines.
For example:
```d
// From std/range/package.d
CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))
alias RvalueElementType = CommonType!(staticMap!(.ElementType,
R));
// ... what's with the . before the ElementType statement? Line
921 says
// .ElementType depends on RvalueElementType. How can they
depend on
// each other? Is this a recursive template thing?
```
and all the other automagic stuff that phobos pulls off to make
ranges work. If that's what's needed to make a custom range
type, then D ranges should come with the warning **don't try this
at home**. (Ali's book made it look so easy that I got sucker in)
Every time I slightly change the inputs to range2, then a
function that operates on *range3* output types blows up with a
helpful message similar to:
```
template
das2.range.PrioritySelect!(PriorityRange!(DasRange!(Tuple!(int,
int)[], int function(Tuple!(int, int)) pure nothrow @nogc @safe,
int function(Tuple!(int, int)) pure nothrow @nogc @safe,
Tuple!(int, int), int), int function() pure nothrow @nogc @safe),
PriorityRange!(DasRange!(Tuple!(int, int)[], int
function(Tuple!(int, int)) pure nothrow @nogc @safe, int
function(Tuple!(int, int)) pure nothrow @nogc @safe, Tuple!(int,
int), int), int function() pure nothrow @nogc
@safe)).PrioritySelect.getReady.filter!((rng) =>
!rng.empty).filter cannot deduce function from argument types
!()(PriorityRange!(DasRange!(Tuple!(int, int)[], int
function(Tuple!(int, int)) pure nothrow @nogc @safe, int
function(Tuple!(int, int)) pure nothrow @nogc @safe, Tuple!(int,
int), int), int function() pure nothrow @nogc @safe),
PriorityRange!(DasRange!(Tuple!(int, int)[], int
function(Tuple!(int, int)) pure nothrow @nogc @safe, int
function(Tuple!(int, int)) pure nothrow @nogc @safe, Tuple!(int,
int), int), int function() pure nothrow @nogc @safe))
```
What the heck is that?
Anyway, you put it all in one bit thing and this is kinda
important: avoid assigning it to anything. You'd ideally do all
the work, from creation to conclusion, all in the big pipeline.
I fell back to using assignments just to make sure range2 values
were saved in a concrete variable so that range3 didn't break
when I changed the lambda that was run by range2 to mutate it's
output elements.
What went in to getting the element to range3's doorstep is a
detail that I shouldn't have to care about inside range3 code,
but am forced to care about it, because changing range2's type,
changes range3's type and triggers really obscure error messages.
(Using interfaces or *gasp* casts, would break the TMI situation.)
So say you want to write it
auto mega_range = range1.range2!(lambda2).range3!(lambda3);
writeln(mega_range);
that'd prolly work, writeln is itself flexible enough, but
you'd prolly be better off doing like
Sure it will work, because writeln isn't some function written by
a new user, it's got all the meta magic.
This way the concrete type never enters into things, it is all
just a detail the compiler tracks to ensure the next consumer
doesn't try to do things the previous step does not support.
It's all just a detail the compiler tracks, until you're not
sending to writeln, but to your own data consumer. Then, you'd
better know all of std.traits and std.meta cause you're going to
need them too implement a range-of-ranges consumer. And by the
way you have to use a range of ranges instead of an array of
ranges because two ranges that look to be identical types,
actually are not identical types and so can't go into the same
array.
Here's an actual (though formatted by me) error message I got
stating that two things were different and thus couldn't share an
array. Can you see the difference? I can't. Please point it
out if you do.
```d
das2/range.d(570,39): Error: incompatible types for (dr_fine) :
(dr_coarse):
das2.range.PriorityRange!(
DasRange!(
Take!(
ZipShortest!(
cast(Flag)false, Result, Generator!(function () @safe =>
uniform(0, 128))
)
),
int function(Tuple!(int, int)) pure nothrow @nogc @safe,
int function(Tuple!(int, int)) pure nothrow @nogc @safe,
Tuple!(int, int),
int
),
int function() pure nothrow @nogc @safe
)
and
das2.range.PriorityRange!(
DasRange!(
Take!(
ZipShortest!(
cast(Flag)false, Result, Generator!(function () @safe =>
uniform(0, 128))
)
),
int function(Tuple!(int, int)) pure nothrow @nogc @safe,
int function(Tuple!(int, int)) pure nothrow @nogc @safe,
Tuple!(int, int),
int
),
int function() pure nothrow @nogc @safe
)
```
But, loops are bad. On the D blog I've seen knowledgeable
people say all loops are bugs.
Meh, don't listen to that nonsense, just write what works for
you. D's strength is that it adapts to different styles and
meets you where you are. Listening to dogmatic sermons about
idiomatic one true ways is throwing that strength away and
likely to kill your personal productivity as you're fighting
your instincts instead of making it work.
Insightful.
Anyway, if you made it this far, you're a saint. Thanks for your
time :)