iota steps, nothrow, naturals and more
bearophile
bearophileHUGS at lycos.com
Sun Mar 16 08:43:50 PDT 2014
The uncooked musings of this post are not very important compared
to other threads.
Currently this doesn't compile because step could be 0:
void main() nothrow {
import std.range: iota;
uint step = 2;
iota(1, 10, step);
}
temp.d(4,9): Error: 'std.range.iota!(int, int, uint).iota' is not
nothrow
But I'd like to use iota/3 (where /3 is its arity) in nothrow
functions too. There several ways to do this. One way is to prove
n can't be zero, but currently D is not this smart, and perhaps
it will never be.
Another solution is to use types (this doesn't compile, I think
because iota has problems with other types):
struct Natural {
uint x;
alias x this;
this(in long input)
pure nothrow in {
assert(input > 0 && input < uint.max);
} body {
this.x = cast(uint)input;
}
}
void main() nothrow {
import std.range: iota;
enum step = 2.Natural;
iota(1.Natural, 10.Natural, step);
}
Here I have defined a positive Natural type. Once iota/3 is aware
of such Phobos-defined Natural type, iota/3 can be specialized to
be nothrow if the step has such type.
Here step is a manifest constant so the pre-condition of its
contructor runs at compile-time. A better solution could be to
add to D "enum pre-conditions" that I have discussed elsewhere:
struct Natural {
uint x;
alias x this;
this(in long input) pure nothrow
enum in {
assert(input > 0 && input < uint.max);
} in {
assert(input > 0 && input < uint.max);
} body {
this.x = cast(uint)input;
}
}
Currently you need enum or something like this to force the
evaluation of Natural ctor pre-condition at compile-time:
enum CT(alias x) = x;
void main() nothrow {
import std.range: iota;
auto step = CT!(Natural(2));
iota(1.Natural, 10.Natural, step);
}
A hypothetical solution is to carry around the interval of
immutable values between lines and use the contracts to shape
such intervals, as asked here:
https://d.puremagic.com/issues/show_bug.cgi?id=10594
void foo(immutable uint step)
nothrow in {
assert(step > 0);
} body {
iota(1, 10, step.Natural);
}
But even implementing issue 10594 is not enough to do this,
because the range value of step is lost at the call
(instantiation) point of Natural.
Some related ideas suggest that __traits could be used to read
the range of a value in user code:
uint x;
static assert(__traits(value_range, x) == [uint.min, uint.max]);
Perhaps there are ways to make this information useful inside an
"enum precondition".
Bye,
bearophile
More information about the Digitalmars-d
mailing list