Bug 9086 – std.typecons.appendIfNotNull() or a better idea
Status
RESOLVED
Resolution
WORKSFORME
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-11-27T18:21:00Z
Last change time
2017-07-02T14:43:52Z
Assigned to
nobody
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2012-11-27T18:21:31Z
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 by bearophile_hugs — 2012-12-18T14:11:54Z
Comment #2 by bearophile_hugs — 2013-12-10T12:07:23Z
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 by astrothayne — 2016-03-16T04:30:45Z
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/[email protected]).
Comment #4 by dlang-bugzilla — 2017-07-02T14:43:52Z
(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