Jump to page: 1 2 3
Thread overview
August 04

I saw Ali's presentation on 'iota'.

I don't want to start another thread about why D has some issues, despite the ammunition provided by the simple fact that you need 37 slides to explain a simple function. If you ask me, it is enough to provide iota for int and maybe long, the rest can be obtained using map & co if you insist. Maybe I want dates. Or fluffy bears.

What I want to highlight, it's the fact that iota seems flawed: it assumes that every type supporting increment operators will generate a nice, consecutive, ordered sequence of values.

Iota specialises on floating point values exactly because of the problem above, but assumes that any other type will nicely behave as long you can put a ++ near a value.

We can ignore the hypotetical situation in which someone will find an unusual usage of ++ (probably there is one person that never imagined that << can be used for console output), but we already have a candidate where iota will fail: CustomFloat from std.numeric.

import std;
void main()
{
    CustomFloat!16 cb = 0.98;
    CustomFloat!16 ce = 1.98099;
    auto x = iota(cb, ce);
    foreach (y; x)
        writeln(y);
    //outputs 0.97998, 1.98047
    //outputs 0.97998 only if ce is 1.98098
}
August 04

On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:

>

What I want to highlight, it's the fact that iota seems flawed: it assumes that every type supporting increment operators will generate a nice, consecutive, ordered sequence of values.

Iota specialises on floating point values exactly because of the problem above, but assumes that any other type will nicely behave as long you can put a ++ near a value.

We can ignore the hypotetical situation in which someone will find an unusual usage of ++ (probably there is one person that never imagined that << can be used for console output), but we already have a candidate where iota will fail: CustomFloat from std.numeric.

Yeah, this is a classic case of what Andrei calls "generality creep".

You start out with the obviously-correct core functionality. Then you start gradually extending it to handle new edge cases. Each one seems reasonable enough on its own, but by the end of the process, you find yourself with an API that's complex and hard to use, wondering where it all went wrong.

August 04

On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:

>

it assumes that every type supporting increment operators will generate a nice, consecutive, ordered sequence of values.

Iota specialises on floating point values exactly because of the problem above, but assumes that any other type will nicely behave as long you can put a ++ near a value.

I disagree the issue is that the std is to general; I believe the issue is that they dont stop adding details to this or that template and try to bandaid issues.

If a 10 line function works in 90% of cases but is hard to extand to be correct 100% of the time; the simpler version would be perferable. But instead they use template constains and traits and who knows what else to try to narrow down edge cases for big chunks of the std.

Iota could be split into a 10 line function called counter that returns int from a range and countby would return x while x<y; x+= z and would fit a float use case which would probaly be anouther 10 lines

If the code was approachable you could allow more edge cases as its would be easier to debug; but theres gaint 1k line functions and most poeple including me treat reading the std as a last resort

August 04

On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:

>

I saw Ali's presentation on 'iota'.
[...]
We can ignore the hypotetical situation in which someone will find an unusual usage of ++ (probably there is one person that never imagined that << can be used for console output), but we already have a candidate where iota will fail: CustomFloat from std.numeric.

Hmm. If for that type ++ does not what you (and iota) expect, then I would ask if that is the fault of iota, or if this type would better NOT provide a ++ operator at all.

August 04
On Thu, Aug 04, 2022 at 05:52:46PM +0000, Paul Backus via Digitalmars-d wrote:
> On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
> > What I want to highlight, it's the fact that iota seems flawed: it assumes that every type supporting increment operators will generate a nice, consecutive, ordered sequence of values.
> > 
> > Iota specialises on floating point values exactly because of the problem above, but assumes that *any* other type will nicely behave as long you can put a ++ near a value.
> > 
> > We can ignore the hypotetical situation in which someone will find an unusual usage of ++ (probably there is one person that never imagined that << can be used for console output), but we already have a candidate where iota will fail: CustomFloat from std.numeric.
> 
> Yeah, this is a classic case of what Andrei calls ["generality creep"][1].
> 
> You start out with the obviously-correct core functionality. Then you start gradually extending it to handle new edge cases. Each one seems reasonable enough on its own, but by the end of the process, you find yourself with an API that's complex and hard to use, wondering where it all went wrong.
> 
> [1]: https://forum.dlang.org/thread/q6plhj$1l9$1@digitalmars.com

I'm probably partly to blame for this. :-/   Cf.:

	https://issues.dlang.org/show_bug.cgi?id=6447

After thinking about this, I think in its barest essentials, the only version of iota we need is the one that takes a single int argument, and that generates that many numbers, starting from 0.  Everything else can be built on top of this with other existing library functions.

For example, if you want a range that starts from 10 and ends at 20, just write:

	iota(10).map!(i => i + 10)

If you want a range that starts at 3 and steps by 5 each time, just write:

	iota(n).map!(i => 3 + i*5)

If you want a floating-point range, just write:

	iota(n).map!(i => cast(double) i)

If you want a range of values of a custom type that generates values via repeated applications of ++, just write:

	MyType startValue = ...;
	auto r = generate!({ return ++startValue; }).take(n);

	// Or, if you want to stop at some sentinel value:
	auto r = generate!({ return ++startValue; })
		.until!(e => e == MyType.sentinelValue);

This could be used, for example, to generate a range over an enum.

If your custom type supports + and you want to use that to provide random access, just do:

	auto r = iota(n).map!(i => MyType.startValue + i);

And so forth.

Everything else is just icing, and not strictly necessary.


T

-- 
Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.
August 04

On Thursday, 4 August 2022 at 19:06:12 UTC, Dom Disc wrote:

>

Hmm. If for that type ++ does not what you (and iota) expect, then I would ask if that is the fault of iota, or if this type would better NOT provide a ++ operator at all.

IMHO, no floatingpoint type should not provide a ++, just like it doesn't provide % or >>. Instead iota with stepwidth should be used.

August 04
On Thursday, 4 August 2022 at 19:06:43 UTC, H. S. Teoh wrote:
> After thinking about this, I think in its barest essentials, the only version of iota we need is the one that takes a single int argument, and that generates that many numbers, starting from 0.  Everything else can be built on top of this with other existing library functions.

Strictly speaking, you don't even need that; all you need is a range that generates the natural numbers, and you can write

    naturals.take(n)

...to get the equivalent of iota(n).

That said, I don't think you have to aim for total minimalism to avoid generality creep here. You just need to know what iota's purpose for inclusion is, so that you can say "no, that's not iota's job; use generate/recurrence/something else instead" when someone proposes an overly-general enhancement.

I wonder if perhaps iota was vulnerable to generality creep in part because it was copied into Phobos from C++'s STL, without a lot of thought given to what specific purpose it served in relation to the rest of the library.
August 04
On Thu, Aug 04, 2022 at 09:31:02PM +0000, Paul Backus via Digitalmars-d wrote:
> On Thursday, 4 August 2022 at 19:06:43 UTC, H. S. Teoh wrote:
> > After thinking about this, I think in its barest essentials, the only version of iota we need is the one that takes a single int argument, and that generates that many numbers, starting from 0. Everything else can be built on top of this with other existing library functions.
> 
> Strictly speaking, you don't even need that; all you need is a range that generates the natural numbers, and you can write
> 
>     naturals.take(n)
> 
> ...to get the equivalent of iota(n).

You don't even need a library prefab for that. Just do:

	auto r = recurrence!"a[n-1] + 1"(0); // equivalent to `naturals`

You can then tack on .take(n) or whatever else to get what you want.

But having to write this every time you need a number range is rather annoying. So there's *some* role here for iota to fill.


> That said, I don't think you have to aim for total minimalism to avoid generality creep here. You just need to know what iota's purpose for inclusion is, so that you can say "no, that's not iota's job; use generate/recurrence/something else instead" when someone proposes an overly-general enhancement.
>
> I wonder if perhaps iota was vulnerable to generality creep in part because it was copied into Phobos from C++'s STL, without a lot of thought given to what specific purpose it served in relation to the rest of the library.

Perhaps.  But the question now is, where do we go from here?  How do we improve what we currently have?


T

-- 
War doesn't prove who's right, just who's left. -- BSD Games' Fortune
August 04

On 8/4/22 3:06 PM, H. S. Teoh wrote:

>

On Thu, Aug 04, 2022 at 05:52:46PM +0000, Paul Backus via Digitalmars-d wrote:

>

On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:

>

What I want to highlight, it's the fact that iota seems flawed: it
assumes that every type supporting increment operators will generate
a nice, consecutive, ordered sequence of values.

Iota specialises on floating point values exactly because of the
problem above, but assumes that any other type will nicely behave
as long you can put a ++ near a value.

We can ignore the hypotetical situation in which someone will find
an unusual usage of ++ (probably there is one person that never
imagined that << can be used for console output), but we already
have a candidate where iota will fail: CustomFloat from std.numeric.

Yeah, this is a classic case of what Andrei calls "generality
creep"
.

You start out with the obviously-correct core functionality. Then you
start gradually extending it to handle new edge cases. Each one seems
reasonable enough on its own, but by the end of the process, you find
yourself with an API that's complex and hard to use, wondering where
it all went wrong.

I'm probably partly to blame for this. :-/ Cf.:

https://issues.dlang.org/show_bug.cgi?id=6447

After thinking about this, I think in its barest essentials, the only
version of iota we need is the one that takes a single int argument, and
that generates that many numbers, starting from 0. Everything else can
be built on top of this with other existing library functions.

For example, if you want a range that starts from 10 and ends at 20,
just write:

iota(10).map!(i => i + 10)

If you want a range that starts at 3 and steps by 5 each time, just write:

iota(n).map!(i => 3 + i*5)
Sorry, I'd rather specify the start, end, and steps, then have to work backwards from the formula what it means. Clarity is important.

Also important is not making the compiler do extra calculations (or the optimizer) when it doesn't have to. A mapping might correctly express intent, but might also fool the optimizer from using a better path that ++ or += would use.

As for the floating point thing, foreach(i; 0.5 .. 7.6) works, it should too for iota.

-Steve

August 05
On Thursday, 4 August 2022 at 22:36:57 UTC, H. S. Teoh wrote:
>
> Perhaps.  But the question now is, where do we go from here?  How do we improve what we currently have?

Phobos v2, I guess? Leave the problematic overloads behind, and tell people upgrading to use a different function if they need something fancier.

Unless there's been a big policy reversal announced at DConf, my impression is that breaking changes to existing Phobos interfaces are off the table.
« First   ‹ Prev
1 2 3