Thread overview
Infinite range of nullable elements
Jul 17, 2015
Roland Hadinger
Jul 17, 2015
Márcio Martins
Jul 17, 2015
Roland Hadinger
Jul 17, 2015
anonymous
Jul 17, 2015
Roland Hadinger
July 17, 2015
At this moment I'm tempted to implement a function taking a range of elements E and returning an infinite range of Nullable!E.

With this function ("cushion" for a lack of better name) I could do:

    auto a = [0,1,2,3,4,5,6,7,8,9];

    foreach (e ; a.cushion.take(20))
        writeln(e); // exactly 20 elements

This would allow chaining together algorithms that need to look ahead in a range.

Here's how I would implement the basic behaviour (could be extended to also forward bidirectional and random access functions):

---
    auto cushion(R)(R r)
        if (isInputRange!R)
    {
        static if (isInfinite!R) { return r; } else {

            struct _Cushion(R)
            {
                R r;

                alias E = ElementType!R;
                alias NE = Nullable!E;

                @property bool empty() { return false; }

                @property NE front()
                {
                    return !r.empty ? NE(r.front) : NE();
                }

                void popFront()
                {
                    if (!r.empty) r.popFront();
                }
            }

            return _Cushion!R(r);
        }
    }

---

I didn't find anything like this Phobos. Did I miss something? Is this a bad idea for some reason?

July 17, 2015
On Friday, 17 July 2015 at 07:42:09 UTC, Roland Hadinger wrote:
> At this moment I'm tempted to implement a function taking a range of elements E and returning an infinite range of Nullable!E.
>
> [...]

Wouldn't it still require the algorithms to check if an element isNull()? Calling get() on a null element will assert().

I'm wondering why it asserts instead of throwing.
July 17, 2015
On Friday, 17 July 2015 at 07:42:09 UTC, Roland Hadinger wrote:
> Here's how I would implement the basic behaviour (could be extended to also forward bidirectional and random access functions):
>
> ---
>     auto cushion(R)(R r)
>         if (isInputRange!R)
>     {
>         static if (isInfinite!R) { return r; } else {
>
>             struct _Cushion(R)
>             {
>                 R r;
>
>                 alias E = ElementType!R;
>                 alias NE = Nullable!E;
>
>                 @property bool empty() { return false; }
>
>                 @property NE front()
>                 {
>                     return !r.empty ? NE(r.front) : NE();
>                 }
>
>                 void popFront()
>                 {
>                     if (!r.empty) r.popFront();
>                 }
>             }
>
>             return _Cushion!R(r);
>         }
>     }
>
> ---
>
> I didn't find anything like this Phobos. Did I miss something?

The building blocks are there. You're `map`ping the original range to `Nullable`, and then you're `chain`ing an infinite range (`cycle`) of nulls behind.

----
import std.range: isInputRange;

auto cushion(R)(R r)
    if (isInputRange!R)
{
    import std.algorithm: map;
    import std.range: chain, cycle, ElementType, only;
    import std.typecons: Nullable;

    alias E = ElementType!R;
    alias NE = Nullable!E;

    return chain(r.map!NE, NE().only.cycle);
}
----
July 17, 2015
On Friday, 17 July 2015 at 10:19:22 UTC, Márcio Martins wrote:
> On Friday, 17 July 2015 at 07:42:09 UTC, Roland Hadinger wrote:
>> At this moment I'm tempted to implement a function taking a range of elements E and returning an infinite range of Nullable!E.
>>
>> [...]
>
> Wouldn't it still require the algorithms to check if an element isNull()?

Naturally. But this check can be done further down the function chain,
so all the functions before will see an infinite range.

I'm currently writing a lexer (big loop with two levels of switch statements inside) that needs to some looking ahead in multiple states. Normally, each of those lookaheads would require a check for "end of input". I want to avoid this because of the large number of states.

July 17, 2015
On Friday, 17 July 2015 at 12:44:57 UTC, anonymous wrote:
> The building blocks are there. You're `map`ping the original range to `Nullable`, and then you're `chain`ing an infinite range (`cycle`) of nulls behind.
>
> ----
> import std.range: isInputRange;
>
> auto cushion(R)(R r)
>     if (isInputRange!R)
> {
>     import std.algorithm: map;
>     import std.range: chain, cycle, ElementType, only;
>     import std.typecons: Nullable;
>
>     alias E = ElementType!R;
>     alias NE = Nullable!E;
>
>     return chain(r.map!NE, NE().only.cycle);
> }
> ----

Nice! I didn't think of using 'chain'.