Bug 8882 – map, filter, iota and zip in pure (and nothrow) functions

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
x86
OS
Windows
Creation time
2012-10-23T16:43:44Z
Last change time
2017-08-30T08:03:11Z
Keywords
rejects-valid
Assigned to
No Owner
Creator
bearophile_hugs

Comments

Comment #0 by bearophile_hugs — 2012-10-23T16:43:44Z
This is not an enhancement request, I think of it as a bug report. This is a spinoff of Issue 8878 To make functional programming in D a better experience, the following functions should be usable in a pure (and possibly nothrow) context: import std.algorithm: map, filter; import std.range: iota, zip; void main() pure nothrow { auto m = map!q{a * a}([1, 2, 3]); auto f = filter!q{ a > 1 }([1, 2, 3]); auto i = iota(1, 10, 2); auto z = zip([1, 2, 3], [10, 20, 30]); } DMD 2.061alpha gives: test.d(4): Error: pure function 'main' cannot call impure function 'map' test.d(5): Error: pure function 'main' cannot call impure function 'filter' test.d(6): Error: pure function 'main' cannot call impure function 'iota' test.d(7): Error: pure function 'main' cannot call impure function 'zip' test.d(4): Error: map is not nothrow test.d(5): Error: filter is not nothrow test.d(6): Error: iota is not nothrow test.d(7): Error: zip is not nothrow test.d(3): Error: function D main 'main' is nothrow yet may throw Note: zip() accepts a StoppingPolicy, so this can't be nothrow: import std.range: zip, StoppingPolicy; void main() pure { auto z = zip(StoppingPolicy.requireSameLength, [1,2,3], [10,20]); foreach (t; z) {} // throws }
Comment #1 by bioinfornatics — 2012-10-24T11:16:25Z
*** Issue 8879 has been marked as a duplicate of this issue. ***
Comment #2 by bearophile_hugs — 2014-01-14T03:16:34Z
The situation has now improved: import std.algorithm: map, filter; import std.range: iota, zip; void main() pure nothrow { foreach (_; map!q{a * a}([1, 2, 3])) {} foreach (_; filter!q{ a > 1 }([1, 2, 3])) {} foreach (_; iota(1, 10, 2)) {} foreach (_; zip([1, 2, 3], [10, 20, 30])) {} } test.d(6): Error: 'std.range.iota!(int, int, int).iota' is not nothrow test.d(7): Error: 'std.range.Zip!(int[], int[]).Zip.empty' is not nothrow test.d(7): Error: 'std.range.Zip!(int[], int[]).Zip.popFront' is not nothrow test.d(3): Error: function 'D main' is nothrow yet may throw zip can throw because of StoppingPolicy.requireSameLength, and iota(x,y,z) throws when z can be 0. iota(x) and iota(x,y) are nothrow. I think the problem with zip() can be solved introducing an api change, or adding a new zip overload that takes StoppingPolicy as first template argument. Fixing iota(x,y,z) is less easy. z is often a value known at compile time, so in theory a new version of iota!z(x, y) can be nothrow at run-time.
Comment #3 by bearophile_hugs — 2014-01-14T13:22:18Z
(In reply to comment #2) > zip can throw because of StoppingPolicy.requireSameLength, and iota(x,y,z) > throws when z can be 0. iota(x) and iota(x,y) are nothrow. even iota(x,y) is not nothrow for floating point arguments: import std.range: iota; void main() nothrow { iota(1.0, 2.0); } test.d(3): Error: 'std.range.iota!(double, double).iota' is not nothrow test.d(2): Error: function 'D main' is nothrow yet may throw
Comment #4 by monarchdodra — 2014-01-16T11:58:06Z
(In reply to comment #2) > The situation has now improved: Yeah, thanks Kenji! > zip can throw because of StoppingPolicy.requireSameLength, and iota(x,y,z) > throws when z can be 0. iota(x) and iota(x,y) are nothrow. > > I think the problem with zip() can be solved introducing an api change, or > adding a new zip overload that takes StoppingPolicy as first template argument. Yes, having a run-time condition is awful IMO. It should always be know compile time. It can also create subtle issues for things such as "hasSlicing", which only works for certain stopping tipes. > Fixing iota(x,y,z) is less easy. z is often a value known at compile time, so > in theory a new version of iota!z(x, y) can be nothrow at run-time. > > even iota(x,y) is not nothrow for floating point arguments: > > > import std.range: iota; > void main() nothrow { > iota(1.0, 2.0); > } > > test.d(3): Error: 'std.range.iota!(double, double).iota' is not nothrow > test.d(2): Error: function 'D main' is nothrow yet may throw Bah, I think illegal input should Error. Problem solved.
Comment #5 by bearophile_hugs — 2014-01-16T13:50:42Z
(In reply to comment #4) > Bah, I think illegal input should Error. Problem solved. Changing an exception -> error changes the API of iota(), but in this case I think there's no need to change the API. "iota(1.0, 2.0);" can be nothrow with small changes in the Phobos code, keeping the same API.
Comment #6 by monarchdodra — 2014-01-16T13:53:55Z
(In reply to comment #5) > (In reply to comment #4) > > > Bah, I think illegal input should Error. Problem solved. > > Changing an exception -> error changes the API of iota() Yes, but I think it's a valid change. > , but in this case I > think there's no need to change the API. "iota(1.0, 2.0);" can be nothrow with > small changes in the Phobos code, keeping the same API. But that's a special case. What about "iota(1.0, 2.0, 0.1)" ?
Comment #7 by razvan.nitu1305 — 2017-08-28T10:19:44Z
The code in the original bug report now compiles. Closing this as fixed.
Comment #8 by petar.p.kirov — 2017-08-29T10:08:06Z
While, the OP code compiles, zip is not yet nothrow. See: ``` import std.algorithm: map, filter; import std.range: iota, zip, array; import std.typecons : tuple; auto get() pure nothrow { auto m = map!q{a * a}([1, 2, 3]); auto f = filter!q{ a > 1 }([1, 2, 3]); auto i = iota(1, 10, 2); auto z = zip([1, 2, 3], [10, 20, 30]); return tuple(m.array, f.array, i.array, z.array); } void main() { import std.stdio; writeln(get()); } ``` test.d(11): Error: function std.array.array!(Zip!(int[], int[])).array is not nothrow test.d(4): Error: nothrow function test.get may throw
Comment #9 by razvan.nitu1305 — 2017-08-30T07:53:43Z
(In reply to ZombineDev from comment #8) > While, the OP code compiles, zip is not yet nothrow. See: > > ``` > import std.algorithm: map, filter; > import std.range: iota, zip, array; > import std.typecons : tuple; > > auto get() pure nothrow > { > auto m = map!q{a * a}([1, 2, 3]); > auto f = filter!q{ a > 1 }([1, 2, 3]); > auto i = iota(1, 10, 2); > auto z = zip([1, 2, 3], [10, 20, 30]); > return tuple(m.array, f.array, i.array, z.array); > } > > void main() > { > import std.stdio; > writeln(get()); > } > ``` > > test.d(11): Error: function std.array.array!(Zip!(int[], int[])).array is > not nothrow > test.d(4): Error: nothrow function test.get may throw There already is issue 11466 [1] which addresses problem, so I suggest we close this and fix that one. [1] https://issues.dlang.org/show_bug.cgi?id=11466
Comment #10 by petar.p.kirov — 2017-08-30T08:03:11Z
(In reply to RazvanN from comment #9) > (In reply to ZombineDev from comment #8) > > While, the OP code compiles, zip is not yet nothrow. See: > > > > ``` > > import std.algorithm: map, filter; > > import std.range: iota, zip, array; > > import std.typecons : tuple; > > > > auto get() pure nothrow > > { > > auto m = map!q{a * a}([1, 2, 3]); > > auto f = filter!q{ a > 1 }([1, 2, 3]); > > auto i = iota(1, 10, 2); > > auto z = zip([1, 2, 3], [10, 20, 30]); > > return tuple(m.array, f.array, i.array, z.array); > > } > > > > void main() > > { > > import std.stdio; > > writeln(get()); > > } > > ``` > > > > test.d(11): Error: function std.array.array!(Zip!(int[], int[])).array is > > not nothrow > > test.d(4): Error: nothrow function test.get may throw > > There already is issue 11466 [1] which addresses problem, so I suggest we > close this and fix that one. > > [1] https://issues.dlang.org/show_bug.cgi?id=11466 I missed that one, so yes, we should close this one, thanks.