On Tuesday, 17 December 2013 at 22:14:41 UTC, H. S. Teoh wrote:
Ah, I see what you're getting at now. I think this idea has a merit on its own; I'm just not sure if it is useful as an actual intermediate
data *type*.

The use over a function would be

1. Contain all of the complexity that working with intervals on (in this case) integers. It's been shown enough times that the straight-forward way of dealing with this is error-prone. 2. Maintain performance characteristics as much as possible. Without an object, a function doing this sort of thing would have to revalidate the bounds each time or, worse, NOT validate the bounds at all (with in contracts, we can validate each time because release code will take the contracts out, but it's still potentially an issue). With an object we can cache any type of validations and/or assertions needed and, potentially, improve performance in some cases. 3. Allow for existing functions to specialize when an interval is given, when appropriate.

But, putting that aside, I think the concept does serve its purpose. It's a pity that the word 'range' already has an assigned meaning in D, because otherwise that would be the best name in this case (i.e., range in the mathematical sense of being a contiguous subset of, say, the number line). So, for the lack of a better name, let's tentatively call it "Bounds" (as in, the set of elements bounded by upper and lower
bounds), which may be defined, at least conceptually, as:

Just to step up your idea to something a bit closer to complete (still have not thrown it into a compiler or anything yet):
http://www.dpaste.dzfl.pl/19c80ff9

(And I really like the idea of a CtInterval, but haven't done anything with it so I've excluded it in the above paste)

It'd also be needed for it to have a simple way to get the smallest acceptable type for the range of values the "between" object could represent. So a for a Between!(uint, int) that would be a uint, and a Between!(int, uint) that would be a long, and so on. Obviously some things _don't_ have acceptable types, such as a Between!(long, ulong) (no integral type currently can actually hold all of those values).

There's nothing wrong with Bounds!(long,ulong); it just won't have an opApply method, that's all. :) It can be conveniently static-if'd out in that case. It can still represent number ranges beyond the current range of built-in types, like [long.min, ulong.max], and you can test for membership with various types. This allows you to test variables of different types, like ints and uints, so the ability to represent such a
range is still useful.

Well, I'm not suggesting that the interval not be allowed... but for things that use that interval, they may produce some sort of output. If they're using the interval to output, then they'll need to know what data type the output needs to be. It'd be convenient if some standard function existed to accomplish that task in a standard way.

The example I'm using for this is if std.random.uniform took in an interval, what would its output be? Obviously, it couldn't output something in [long.min, ulong.max], but it's possible it could spit out an answer in [byte.min, ubyte.max] since a short could contain all of those values.

Something like this, like I showed, could be used to pass to other functions like std.random.uniform which request a range to generate. Or you should be able to pass it to something like std.algorithm.find,
std.algorithm.count, etc (predicates that take one parameter).

While you *could* implement the input range API for the Bounds struct for this purpose, it's probably better to define special overloads for find and count, since you really don't want to waste time iterating over elements instead of just directly computing the narrowed Bounds struct
or subtracting the lower bound from the upper, respectively. For
example:

Sorry, confusion based on using the word "range" again. When I said range, I meant bounds/interval in this case. Functions that request some sort of interval or bounds should use interval instead of trying to do anything on its own (since the "do your own thing" is increasingly being found to be errorprone).

So, something like this should work:

    unittest
    {
        import std.algorithm;
        assert(
            find!"a in b"([5, 6, 2, 9], interval(1, 4))
                == [2, 9]);
        // uses std.algorithm.find

        assert(
            count!"a in b"([5, 6, 1, 3, 9, 7, 2], interval(1,3))
                == 3);
        // uses std.algorithm.count

        import std.random;
        foreach(_; 0..10000)
            assert(uniform(interval(1,5)) in interval(1,5));
        // Nice assertion, right?
    }

It might also be useful in some circumstances to be able to know how many values are in the interval (sort of like a "length" or "size") but if you have an interval of [long.min, ulong.max] ... well, you know the problem.

Considering what Andrei said, we might could expand this concept to support the interval arithmetic. We'd also need to be able to support intervals like (-oo, oo), (-oo, x], [x, oo) ... where the membership test returns true, <=x, and >=x respectively (while taking care of the issues that exist with signed/unsigned comparisons, obviously). That said, not all functions will want to handle those types of intervals (std.random.uniform, for instance).

Reply via email to