On Friday, 27 December 2013 at 20:30:52 UTC, Ivan Kazmenko wrote:
Hmm?.. From my experience, attempting to use a range in a wrong way usually results in a compilation error. For example, I can't do
n.iota.map!(_ => readln).sort())
since MapResult isn't a random access range with swappable elements. I can instead do
n.iota.map!(_ => readln).array().sort())
and it allocates an array and works as expected. So, how do I misuse that range?

Yes, the idea is that ranges only present interfaces that make sense, so cases of misuse will result in a compilation error. However, hacks like using map with functions that ignore their argument(s) throws that out of the window: `r` in `auto r = n.iota.map!(_ => readln);` claims to support forward, bidirectional and random access (all read-only, as the argument function returns by value) as well as slicing, but none of these make any sense; all access primitives do exactly the same thing, with the result being different every time. Even the simplest invariants fail, such as `r.front == r.front`, and `popFront`, `popBack` and slicing only has a binary effect, whether or not the range is empty yet.

Hmm. For example, that could be a RNG emitting (a range of) random numbers, then "empty" is always false. But we still want a new random number each time. Something like
n.iota.map!(_ => uniform(0, 10))

That would only provide `n` random numbers, not an infinite number.

All the random number generator types in `std.random` are infinite forward ranges of random numbers, which is completely fine. For any PRNG `r`, `r.front == r.front` is true, and remains the same number until `r.popFront()`, it correctly has no length and is always non-empty (infinite range), and `r.save` works correctly etc.

So, something like
n.iota.map !(_ => readln).writeln;
is bad style but
writeln (n.iota.map !(_ => readln));
better shows what's the main action?  Makes sense for me.

No, it has nothing to do with syntax. The two examples are completely equivalent, and the only problem is that it breaks the invariant that the result of map's transformation function should be derived from the arguments it was given. The fact that the transformation function is impure is not in itself a problem: pure functions can also ignore arguments, and impure functions can return consistent results while still being necessarily impure.

Perhaps there's a wholly different way of thinking about this in which the first definition makes much more sense than then second one from the start. If so, please share it.

All you have to do is look at the signature of the function, which is the primary part of its documentation:

Repeat!T repeat(T)(T value);

It takes one value of any type T, not a function pointer or delegate that returns T. Even if you give it a function pointer or delegate (which your example does not), it will simply repeat that function pointer or delegate, never calling it.

As I already explained, `readln.repeat(n)` is just a different way of writing `readln().repeat(n)` which in turn is also equivalent to `repeat(readln(), n)`. This should make it perfectly clear what it does - `readln` is called and its return value is passed to `repeat`. Barring one relatively obscure exception[1], this is the only way to interpret the expression regardless of the signature of the function, as a consequence of basic languages rules common to the entire C family of programming languages.

[1] ... in D we have something (slightly controversial) called the `lazy` parameter storage class, but when used, it is clearly visible in the signature of the function. http://dlang.org/function.html#parameters

Reply via email to