D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 9086 - std.typecons.appendIfNotNull() or a better idea
Summary: std.typecons.appendIfNotNull() or a better idea
Status: RESOLVED WORKSFORME
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: All All
: P2 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-11-27 18:21 UTC by bearophile_hugs
Modified: 2017-07-02 14:43 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2012-11-27 18:21:31 UTC
A problem with std.typecons.Nullable is that the D type system is not powerful enough to see this is correct code:


import std.typecons;
void main() nothrow {
    alias Nothing = Nullable!int;
    Nothing[] items;
    foreach (i; 0 .. 10)
        items ~= (i % 2) ? Nothing(i) : Nothing();

    int[] result;
    foreach (x; items)
        if (!x.isNull)
            result ~= x.get; // Error: x.get is not nothrow
}


Another problem is that "x.get" verifies the enforcement even in the "then" clause, this is wasted computation, because x was tested already.


One common enough use case for Nullable is to create arrays of Nullables and then filter out the null values.

Currently code like this can't be nothrow:

items.filter!(x => !x.isNull)().map!(x => x.get)()



So I have created a little function named appendIfNotNull() that is nothrow:

import std.typecons;

void appendIfNotNull(T)(ref T[] items, Nullable!T x) pure nothrow {
    try {
        if (!x.isNull)
            items ~= x.get;
    } catch (Exception e) // enforce() exception.
        assert(0);
}

// Demo code --------------------------
import std.stdio, std.algorithm;

alias Nothing = Nullable!int;

Nothing[] foo(int n) nothrow {
    typeof(return) result;
    foreach (i; n .. n + 6)
        result ~= (i % 2) ? Nothing(i) : Nothing();
    return result;
}

int[] bar() nothrow {
    typeof(return) numbers;
    foreach (i; 0 .. 3)
        foreach (x; foo(i * 10))
            numbers.appendIfNotNull(x);
    return numbers;
}

void main() {
    writeln(bar());
    foo(1).filter!(x => !x.isNull)().map!(x => x.get)().writeln();
}


appendIfNotNull() is nothrow, but it wastes efficiency. So here is a simpler and more efficient implementation for the std.typecons module (because _isNull and _value fields are private):


void appendIfNotNull(T)(ref T[] items, Nullable!T x) nothrow {
    if (!x._isNull)
        items ~= x._value;
}


Improvements for appendIfNotNull:
- items instead of a T[] should be a range that supports appending.
- x should be allowed to be one of both types of Nullables.


appendIfNotNull() is not a very generic function, something better can be invented, usable in more cases. More general ideas are welcome.
Comment 1 bearophile_hugs 2012-12-18 14:11:54 UTC
See also here for some usage patterns of the Scala Option type:

http://blog.tmorris.net/scalaoption-cheat-sheet/
Comment 2 bearophile_hugs 2013-12-10 12:07:23 UTC
A comment by Max Klyga:

> Actually for Option there is no need for special casing as it acts as 
> an input range of one or zero elements, so all map/filter/etc. will 
> just work
Comment 3 Thayne 2016-03-16 04:30:45 UTC
One way to deal with this could be to add an Option/Maybe type, or define a function get a range from a Nullable (or add some sort map and flatmap functions) (see http://forum.dlang.org/thread/l87ivq$263r$1@digitalmars.com).
Comment 4 Vladimir Panteleev 2017-07-02 14:43:52 UTC
(In reply to bearophile_hugs from comment #0)
> A problem with std.typecons.Nullable is that the D type system is not
> powerful enough to see this is correct code:

This example now compiles fine.

Fixed in https://github.com/dlang/phobos/pull/1103