Feature proposal: Range operator with step

I like this a lot but I assure you there will be some pushback about readability of intent.

-- "Cassandra"

···

On Apr 6, 2016, at 11:10 AM, Dave Abrahams <dabrahams@apple.com> wrote:
Again, I wasn't trying to suggest any of the solutions listed there.
The point I was making was that we don't have enough information to
design more than

   (0..<200).striding(by: -2)

I think a lightbulb just went on for me:

You're talking about expressing something in the vein of
`(0..<200).striding(by: -2)`, which has I'm sure many use cases, and which
isn't straightforward to express with the current free function--I hadn't
considered that.

Meanwhile, I was trying to talk about something like `stride(from: 200, to:
0, by: -2)`, which is easily expressed today but isn't straightforward at
all to preserve with only ranges. Clearly, given that this is what's on
offer currently, someone who designed the language thinks (or thought) it's
of some use.

In the absence of information as to which is more in demand, couldn't we
have both? If it must be a method on a range, then I would advocate for
having what seems to be an utterly reasonable set of options for striding
backwards:

(0...200).striding(by: -2) // [a, b]
(0..<200).striding(by: -2) // [a, b)
(0<..200).striding(by: -2) // (a, b]
···

On Wed, Apr 6, 2016 at 12:10 PM Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Wed Apr 06 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

>> For example, there are all kinds of other ways to slice this:
>>
>> stride(over: 0..<200, by: -2)
>
> This seems like a particularly good solution. The way I understand it
> at least, it would allow ranges to always be ordered, with the only
> difference being whether it went start-to-end or end-to-start,
> determined by the stride's sign.

This is no different in principle from

     (0..<200).striding(by: -2)

Again, I wasn't trying to suggest any of the solutions listed there.
The point I was making was that we don't have enough information to
design more than

    (0..<200).striding(by: -2)

> It would also avoid the need for additional range operators. The main
> reason you would need `>..` is so you could say
> `array.endIndex>..array.startIndex`, but by using the sign to decide
> which direction to stride over the range, you instead stride over
> `array.startIndex..<array.endIndex`, which is exactly what we already
> have.
>
> Unfortunately, moving away from `stride(from:to/through:by:)` would
> kind of mess up an idea I've been developing for providing an
> "induction sequence" to replace the more complicated C-style for use
> cases, but I suppose that's the way it goes...
>
> (Link to that:
https://gist.github.com/brentdax/b24dd89a770d9fe376984498d3185187\)

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think a lightbulb just went on for me:

You're talking about expressing something in the vein of `(0..<200).striding(by:
-2)`, which has I'm sure many use cases, and which isn't straightforward to
express with the current free function--I hadn't considered that.

Meanwhile, I was trying to talk about something like `stride(from: 200, to: 0,
by: -2)`, which is easily expressed today but isn't straightforward at all to
preserve with only ranges. Clearly, given that this is what's on offer
currently, someone who designed the language thinks (or thought) it's of some
use.

That someone was me, and I explained that it wasn't an extremely
deeply-considered decision.

In the absence of information as to which is more in demand, couldn't
we have both?

That's not how we make decisions about what should be in the language or
standard library. We need to make choices based on (at least educated
guesses about) what people need, or we'll end up with a sprawling mess.

If it must be a method on a range,

It's not that it must be, but having such a method tends to reduce API
surface area. We prefer methods to free functions.

then I would advocate for having what seems to be an utterly
reasonable set of options for striding backwards:

(0...200).striding(by: -2) // [a, b]
(0..<200).striding(by: -2) // [a, b)
(0<..200).striding(by: -2) // (a, b]

And I'm trying to say that without a more compelling reason to introduce
`<..`, I don't want to do it. I'd like to know that `<..` is useful
outside the domain of striding, for example. Use-cases, anyone?

Reasons we might not need it: the cases where it's important are much
more likely to be notionally continuous domains (e.g. floats), since you
can always write

    ((0+1)...200).striding(by: -2)

and if you need the floating version there's always

   (0.0...200.0).striding(by: -2).lazy.filter { $0 != 0.0 }

which probably optimizes down to the same code.

One question that I *do* think we should answer, is whether the elements
of

    (0..<199).striding(by: -2)

are even or odd.

···

on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

On Wed, Apr 6, 2016 at 12:10 PM Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:

    on Wed Apr 06 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

    >> For example, there are all kinds of other ways to slice this:
    >>
    >> stride(over: 0..<200, by: -2)
    >
    > This seems like a particularly good solution. The way I understand it
    > at least, it would allow ranges to always be ordered, with the only
    > difference being whether it went start-to-end or end-to-start,
    > determined by the stride's sign.

    This is no different in principle from

    (0..<200).striding(by: -2)

    Again, I wasn't trying to suggest any of the solutions listed there.
    The point I was making was that we don't have enough information to
    design more than

    (0..<200).striding(by: -2)

    > It would also avoid the need for additional range operators. The main
    > reason you would need `>..` is so you could say
    > `array.endIndex>..array.startIndex`, but by using the sign to decide
    > which direction to stride over the range, you instead stride over
    > `array.startIndex..<array.endIndex`, which is exactly what we already
    > have.
    >
    > Unfortunately, moving away from `stride(from:to/through:by:)` would
    > kind of mess up an idea I've been developing for providing an
    > "induction sequence" to replace the more complicated C-style for use
    > cases, but I suppose that's the way it goes...
    >
    > (Link to that:
    https://gist.github.com/brentdax/b24dd89a770d9fe376984498d3185187\)

    --
    Dave
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org
    https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

        One question that I *do* think we should answer, is whether the elements
        of

        (0..<199).striding(by: -2)

        are even or odd.

    Odd. I don’t believe that many real use cases care, but odd is more
    efficient from a performance perspective. Needs to be documented clearly,
    however.

Sorry, I was thinking of (0…199).striding(by: -2).

For the (0..<199) case, Erica’s assessment

which is...?

···

on Wed Apr 06 2016, Stephen Canon <scanon-AT-apple.com> wrote:

    On Apr 6, 2016, at 11:20 AM, Stephen Canon via swift-evolution > <swift-evolution@swift.org> wrote:
        On Apr 6, 2016, at 11:16 AM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:

seems about right, though it isn’t at all obvious how it generalizes
to floating point strides.

– Steve

--
Dave

-- E

···

On Apr 6, 2016, at 12:30 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Apr 06 2016, Stephen Canon <scanon-AT-apple.com> wrote:

For the (0..<199) case, Erica’s assessment

which is...?

I think a lightbulb just went on for me:

You're talking about expressing something in the vein of `(0..<200).striding(by:
-2)`, which has I'm sure many use cases, and which isn't straightforward to
express with the current free function--I hadn't considered that.

Meanwhile, I was trying to talk about something like `stride(from: 200, to: 0,
by: -2)`, which is easily expressed today but isn't straightforward at all to
preserve with only ranges. Clearly, given that this is what's on offer
currently, someone who designed the language thinks (or thought) it's of some
use.

That someone was me, and I explained that it wasn't an extremely
deeply-considered decision.

Fair enough. Though your decision may not have been deeply considered,
I wouldn't say it was an ill-considered one given that there wasn't
(to my knowledge) any great clamor against it subsequently.

In the absence of information as to which is more in demand, couldn't
we have both?

That's not how we make decisions about what should be in the language or
standard library. We need to make choices based on (at least educated
guesses about) what people need, or we'll end up with a sprawling mess.

Well, to elicit the kind of feedback that would help determine user
needs, I would suggest that (when the eventually reconsidered syntax
is proposed) this change should be highlighted explicitly as a feature
removal. IMO, it wouldn't be otherwise immediately apparent from a
quick glance that revising `stride(from: 0, to: 10, by: 1)` to
`(0..<10).striding(by: 1)` necessarily entails deletion of backwards
strides from upper bound to-and-not-through lower bound.

If it must be a method on a range,

It's not that it must be, but having such a method tends to reduce API
surface area. We prefer methods to free functions.

then I would advocate for having what seems to be an utterly
reasonable set of options for striding backwards:

(0...200).striding(by: -2) // [a, b]
(0..<200).striding(by: -2) // [a, b)
(0<..200).striding(by: -2) // (a, b]

And I'm trying to say that without a more compelling reason to introduce
`<..`, I don't want to do it. I'd like to know that `<..` is useful
outside the domain of striding, for example. Use-cases, anyone?

Well, my use case (an actual one) is supremely mundane. I'm doing some
scientific computing and I need to deal with numeric intervals. Some
of them are closed, some of them are open at one end, and some at the
other.

You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).

Do we need fully-open ranges too?

···

on Wed Apr 06 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

On Wed, Apr 6, 2016 at 1:16 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

Reasons we might not need it: the cases where it's important are much
more likely to be notionally continuous domains (e.g. floats), since you
can always write

    ((0+1)...200).striding(by: -2)

and if you need the floating version there's always

   (0.0...200.0).striding(by: -2).lazy.filter { $0 != 0.0 }

which probably optimizes down to the same code.

One question that I *do* think we should answer, is whether the elements
of

    (0..<199).striding(by: -2)

are even or odd.

On Wed, Apr 6, 2016 at 12:10 PM Dave Abrahams via swift-evolution >>> <swift-evolution@swift.org> wrote:

    on Wed Apr 06 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

    >> For example, there are all kinds of other ways to slice this:
    >>
    >> stride(over: 0..<200, by: -2)
    >
    > This seems like a particularly good solution. The way I understand it
    > at least, it would allow ranges to always be ordered, with the only
    > difference being whether it went start-to-end or end-to-start,
    > determined by the stride's sign.

    This is no different in principle from

    (0..<200).striding(by: -2)

    Again, I wasn't trying to suggest any of the solutions listed there.
    The point I was making was that we don't have enough information to
    design more than

    (0..<200).striding(by: -2)

    > It would also avoid the need for additional range operators. The main
    > reason you would need `>..` is so you could say
    > `array.endIndex>..array.startIndex`, but by using the sign to decide
    > which direction to stride over the range, you instead stride over
    > `array.startIndex..<array.endIndex`, which is exactly what we already
    > have.
    >
    > Unfortunately, moving away from `stride(from:to/through:by:)` would
    > kind of mess up an idea I've been developing for providing an
    > "induction sequence" to replace the more complicated C-style for use
    > cases, but I suppose that's the way it goes...
    >
    > (Link to that:
    https://gist.github.com/brentdax/b24dd89a770d9fe376984498d3185187\)

    --
    Dave
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org
    https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

Below?

···

on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:

You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).

Do we need fully-open ranges too?

I haven't encountered a need for open ranges, but I would expect that
other applications in scientific computing could make use of them.
I rather like Pyry's suggestions below.

--
Dave

        You if you need to represent `<..` intervals in scientific computing,
            that's a pretty compelling argument for supporting them.

                        I'd like to be able to represent any of those as
                Intervals-which-are-now-Ranges. It makes sense to do so because
                the
                things I want to do with them, such as clamping and testing if
                some
                value is contained, are exactly what Intervals-now-Ranges
                provide.
                Looking around, it seems many other languages provide only what
                Swift
                currently does, but Perl does provide `..`, `..^`, `^..`, and
                `^..^`
                (which, brought over to Swift, would be `...`, `..<`, `<..`, and
                `<.<`).

            Do we need fully-open ranges too?

        I haven't encountered a need for open ranges, but I would expect that
        other applications in scientific computing could make use of them.
        I rather like Pyry's suggestions below.

    Below?

Logically in time below.

Oh! In my application, time flows downward.

I believe the following is a valid conversion of the Xiaodi Wu below into the
Dave A domain.

    I think a sensible specification would be that with a positive step size,
    the count starts from the lower bound, and with a negative one, it starts
    from the upper bound (inclusive or exclusive). Thus, the following examples
    should cover all the corner cases:

    (0 ... 9).striding(by: 2) == [0, 2, 4, 6, 8]
    (0 ..< 9).striding(by: 2) == [0, 2, 4, 6, 8]
    (0 <.. 9).striding(by: 2) == [2, 4, 6, 8]
    (0 <.< 9).striding(by: 2) == [2, 4, 6, 8]

    (0 ... 9).striding(by: 3) == [0, 3, 6, 9]
    (0 ..< 9).striding(by: 3) == [0, 3, 6]
    (0 <.. 9).striding(by: 3) == [3, 6, 9]
    (0 <.< 9).striding(by: 3) == [3, 6]

    (0 ... 9).striding(by: -2) == [9, 7, 5, 3, 1]
    (0 ..< 9).striding(by: -2) == [7, 5, 3, 1]
    (0 <.. 9).striding(by: -2) == [9, 7, 5, 3, 1]
    (0 <.< 9).striding(by: -2) == [7, 5, 3, 1]

    (0 ... 9).striding(by: -3) == [9, 6, 3, 0]
    (0 ..< 9).striding(by: -3) == [6, 3, 0]
    (0 <.. 9).striding(by: -3) == [9, 6, 3]
    (0 <.< 9).striding(by: -3) == [6, 3]

These all look reasonable to me.

    Lastly, if you want the positive stride reversed, you'd do just that:

    (0 ... 9).striding(by: 2).reverse() == [8, 6, 4, 2, 0]

Also reasonable.

···

on Wed Apr 06 2016, Erica Sadun <erica-AT-ericasadun.com> wrote:

    On Apr 6, 2016, at 3:05 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:
    on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:
        On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:
    On Apr 6, 2016, at 2:29 PM, Pyry Jahkola via swift-evolution > <swift-evolution@swift.org> wrote:

--
Dave

Agreed.

– Steve

···

On Apr 6, 2016, at 2:25 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Apr 06 2016, Erica Sadun <erica-AT-ericasadun.com> wrote:

   On Apr 6, 2016, at 3:05 PM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

   on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

       On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

       You if you need to represent `<..` intervals in scientific computing,
           that's a pretty compelling argument for supporting them.

                       I'd like to be able to represent any of those as
               Intervals-which-are-now-Ranges. It makes sense to do so because
               the
               things I want to do with them, such as clamping and testing if
               some
               value is contained, are exactly what Intervals-now-Ranges
               provide.
               Looking around, it seems many other languages provide only what
               Swift
               currently does, but Perl does provide `..`, `..^`, `^..`, and
               `^..^`
               (which, brought over to Swift, would be `...`, `..<`, `<..`, and
               `<.<`).

           Do we need fully-open ranges too?

       I haven't encountered a need for open ranges, but I would expect that
       other applications in scientific computing could make use of them.
       I rather like Pyry's suggestions below.

   Below?

Logically in time below.

Oh! In my application, time flows downward.

I believe the following is a valid conversion of the Xiaodi Wu below into the
Dave A domain.

   On Apr 6, 2016, at 2:29 PM, Pyry Jahkola via swift-evolution >> <swift-evolution@swift.org> wrote:

   I think a sensible specification would be that with a positive step size,
   the count starts from the lower bound, and with a negative one, it starts
   from the upper bound (inclusive or exclusive). Thus, the following examples
   should cover all the corner cases:

   (0 ... 9).striding(by: 2) == [0, 2, 4, 6, 8]
   (0 ..< 9).striding(by: 2) == [0, 2, 4, 6, 8]
   (0 <.. 9).striding(by: 2) == [2, 4, 6, 8]
   (0 <.< 9).striding(by: 2) == [2, 4, 6, 8]

   (0 ... 9).striding(by: 3) == [0, 3, 6, 9]
   (0 ..< 9).striding(by: 3) == [0, 3, 6]
   (0 <.. 9).striding(by: 3) == [3, 6, 9]
   (0 <.< 9).striding(by: 3) == [3, 6]

   (0 ... 9).striding(by: -2) == [9, 7, 5, 3, 1]
   (0 ..< 9).striding(by: -2) == [7, 5, 3, 1]
   (0 <.. 9).striding(by: -2) == [9, 7, 5, 3, 1]
   (0 <.< 9).striding(by: -2) == [7, 5, 3, 1]

   (0 ... 9).striding(by: -3) == [9, 6, 3, 0]
   (0 ..< 9).striding(by: -3) == [6, 3, 0]
   (0 <.. 9).striding(by: -3) == [9, 6, 3]
   (0 <.< 9).striding(by: -3) == [6, 3]

These all look reasonable to me.

Thanks. In that case, I suggest that we entertain two separate
proposals:

1. add the .striding(by: n) method.
2. add the other range operators.

Though they both have obvious benefits, I expect #1 is a much easier
sell than #2, which is one good reason to separate them.

···

on Wed Apr 06 2016, Erica Sadun <erica-AT-ericasadun.com> wrote:

    On Apr 6, 2016, at 3:25 PM, Dave Abrahams <dabrahams@apple.com> wrote:

    These all look reasonable to me.

        Lastly, if you want the positive stride reversed, you'd do just that:

        (0 ... 9).striding(by: 2).reverse() == [8, 6, 4, 2, 0]

    Also reasonable.

    --
    Dave

Unless there's a compelling reason to fight here, it looks like the
opinion against where I'm standing is pretty overwhelming at least in
this subgroup. To simplify things going forward (and to avoid compiler
warnings, which as Dave A points out is probably an indication of bad
design more than bad users), I'm willing to adopt in as well.

--
Dave

From a purely numerically aesthetic point of view, I'd much prefer ranges to be
openable and closable at both ends.

My primary use-case has been teaching math using playgrounds but I'm sure
there are lots of other real-world situations more specific to common numerical
method tasks.

-- E

···

On Apr 6, 2016, at 2:28 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

From a purely numerically aesthetic point of view, I'd much prefer ranges to be
openable and closable at both ends.

My primary use-case has been teaching math using playgrounds but I'm sure
there are lots of other real-world situations more specific to common numerical
method tasks.

By coincidence, a Perl hacker I know commented on Twitter yesterday that he thought 1-based arrays were the way to go in the 21st century. Somebody replying to that suggestion linked to a note by Dijkstra that's relevant to this conversation: <https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html&gt;

I'd suggest everyone in this discussion should read it—it's only about 700 words—but to summarize:

  1. The semantic Swift refers to as `..<` is the most natural range convention.
  2. Relatedly, zero-based indexing is the most natural indexing convention.

If we agree with Dijkstra's logic, then the only reason to support `>..` is for ranges where start > end—that is, when we're constructing a reversed range. But if we decide to support striding backwards by using a forward range and a negative stride, then that covers the reverse use case. Thus, we would need neither additional range operators, nor reversed ranges.

As for the `range.striding(by:)` vs `stride(over:by:)` question, my concerns there are, to be honest, mainly aesthetic. The need for parentheses around the range operator is more or less unavoidable, but I think they make the construct very ugly. However, I also think that the `stride(over:by:)` syntax (or, for that matter `stride(from:to:by:)`) look more constructor-y (they are only *not* constructors now because of the overloading), and I think it opens us up to parallel constructs like the `induce(from:while:by:)` function I've been working on.

···

--
Brent Royal-Gordon
Architechies

From a purely numerically aesthetic point of view, I'd much prefer ranges to be
openable and closable at both ends.

My primary use-case has been teaching math using playgrounds but I'm sure
there are lots of other real-world situations more specific to common numerical
method tasks.

By coincidence, a Perl hacker I know commented on Twitter yesterday
that he thought 1-based arrays were the way to go in the 21st
century. Somebody replying to that suggestion linked to a note by
Dijkstra that's relevant to this conversation:
<https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html&gt;

I'd suggest everyone in this discussion should read it—it's only about 700 words—but to summarize:

  1. The semantic Swift refers to as `..<` is the most natural range convention.
  2. Relatedly, zero-based indexing is the most natural indexing convention.

If we agree with Dijkstra's logic, then the only reason to support
`>..` is for ranges where start > end—that is, when we're constructing
a reversed range.

I (am familiar with and) agree with Dijkstra's logic, but not with your
conclusion about it. The fact that one representation is more natural
for most common computing tasks doesn't mean it's not worth supporting
the other representations.

···

on Wed Apr 06 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

But if we decide to support striding backwards by using a forward
range and a negative stride, then that covers the reverse use
case. Thus, we would need neither additional range operators, nor
reversed ranges.

As for the `range.striding(by:)` vs `stride(over:by:)` question, my
concerns there are, to be honest, mainly aesthetic. The need for
parentheses around the range operator is more or less unavoidable, but
I think they make the construct very ugly. However, I also think that
the `stride(over:by:)` syntax (or, for that matter
`stride(from:to:by:)`) look more constructor-y (they are only *not*
constructors now because of the overloading), and I think it opens us
up to parallel constructs like the `induce(from:while:by:)` function
I've been working on.

--
Dave

You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).

Do we need fully-open ranges too?

I haven't encountered a need for open ranges, but I would expect that
other applications in scientific computing could make use of them.
I rather like Pyry's suggestions below.

Below?

Logically in time below.

I believe the following is a valid conversion of the Xiaodi Wu below into the Dave A domain.

···

On Apr 6, 2016, at 3:05 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com <http://xiaodi.wu-at-gmail.com/&gt;&gt; wrote:

On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

On Apr 6, 2016, at 2:29 PM, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

I think a sensible specification would be that with a positive step size, the count starts from the lower bound, and with a negative one, it starts from the upper bound (inclusive or exclusive). Thus, the following examples should cover all the corner cases:

    (0 ... 9).striding(by: 2) == [0, 2, 4, 6, 8]
    (0 ..< 9).striding(by: 2) == [0, 2, 4, 6, 8]
    (0 <.. 9).striding(by: 2) == [2, 4, 6, 8]
    (0 <.< 9).striding(by: 2) == [2, 4, 6, 8]

    (0 ... 9).striding(by: 3) == [0, 3, 6, 9]
    (0 ..< 9).striding(by: 3) == [0, 3, 6]
    (0 <.. 9).striding(by: 3) == [3, 6, 9]
    (0 <.< 9).striding(by: 3) == [3, 6]

    (0 ... 9).striding(by: -2) == [9, 7, 5, 3, 1]
    (0 ..< 9).striding(by: -2) == [7, 5, 3, 1]
    (0 <.. 9).striding(by: -2) == [9, 7, 5, 3, 1]
    (0 <.< 9).striding(by: -2) == [7, 5, 3, 1]

    (0 ... 9).striding(by: -3) == [9, 6, 3, 0]
    (0 ..< 9).striding(by: -3) == [6, 3, 0]
    (0 <.. 9).striding(by: -3) == [9, 6, 3]
    (0 <.< 9).striding(by: -3) == [6, 3]

Lastly, if you want the positive stride reversed, you'd do just that:

    (0 ... 9).striding(by: 2).reverse() == [8, 6, 4, 2, 0]

— Pyry

Unless there's a compelling reason to fight here, it looks like the opinion against
where I'm standing is pretty overwhelming at least in this subgroup. To simplify
things going forward (and to avoid compiler warnings, which as Dave A points out
is probably an indication of bad design more than bad users), I'm willing to adopt
in as well.

-- E

···

On Apr 6, 2016, at 3:25 PM, Dave Abrahams <dabrahams@apple.com> wrote:

These all look reasonable to me.

   Lastly, if you want the positive stride reversed, you'd do just that:

   (0 ... 9).striding(by: 2).reverse() == [8, 6, 4, 2, 0]

Also reasonable.

--
Dave

I haven't encountered a need for open ranges, but I would expect that
other applications in scientific computing could make use of them.
I rather like Pyry's suggestions below. These would represent an
expansive fleshing out of ranges. They really start pulling their
weight for floating point bounds; of course, I'd wager that ... and
..< would still be the most used by far.

···

On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).

Do we need fully-open ranges too?

Sorry, chronologically, above; in terms of relative positions in the
thread, I think, below, since Pyry's suggests followed on from the
time I started writing. Ha, and Erica (above/below) just filled in
what I meant to say.

···

On Wed, Apr 6, 2016 at 4:05 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

On Wed, Apr 6, 2016 at 3:28 PM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

You if you need to represent `<..` intervals in scientific computing,
that's a pretty compelling argument for supporting them.

I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).

Do we need fully-open ranges too?

I haven't encountered a need for open ranges, but I would expect that
other applications in scientific computing could make use of them.
I rather like Pyry's suggestions below.

Below?

I (am familiar with and) agree with Dijkstra's logic, but not with your
conclusion about it. The fact that one representation is more natural
for most common computing tasks doesn't mean it's not worth supporting
the other representations.

I'm not saying that Dijkstra proves that we don't need any other range operators. Rather, I'm saying that he demonstrates why supporting `..<` but not `<..` is not arbitrary or capricious. Dijkstra's argument *permits* us to privilege `..<` as uniquely important, but doesn't *force* us to do so.

To another person just now, you said:

He was talking about ranges of integer indices, though, and even
more-specifically about how to address arrays. Range<Bound> is a more
general concept that applies to much more than indices. Once you
involve floating point (and rationals, and patterns for matching,
e.g. UnicodeScalar("a")..."z"), the conclusions no longer apply.

I actually think he was talking a little more broadly than that—essentially, he was discussing ordered, discrete types. In principle, the same argument applies to UnicodeScalars, but not to floating-point numbers (unless you use treat floats as a discrete type using `nextafter` as the `successor()` operation, which is coherent but not very useful in practice). Having said that, I *do* think that `...` is in practice quite useful for many types. I'm less certain that `<..` or `<.<` are.

* * *

By the way, another reason to have `stride` as a free function is that I think some types need a "strider", an instance which performs the striding.

That was the conclusion I came to when I started experimenting with striding over NSDates a week or two ago. The best design I could come up with looked like this:

  calendar.using(.Day).stride(from: startDate, to: endDate, by: 1)

The `start` and `end` parameters could be grouped together into a single parameter to match `stride(over:by:)`, but you can't put the calendar or the unit into the stride—without them, there is no coherent way to calculate the distance between two dates.

So if some types need a strider, and will need to have the method structured as `strider.stride(something:by:)`, it seems like the free function version for types which *don't* need a strider ought to be `stride(something:by:)`. The `something.striding(by:)` design can't be easily adapted to this situation.

···

--
Brent Royal-Gordon
Architechies

I (am familiar with and) agree with Dijkstra's logic, but not with your
conclusion about it. The fact that one representation is more natural
for most common computing tasks doesn't mean it's not worth supporting
the other representations.

I'm not saying that Dijkstra proves that we don't need any other range
operators. Rather, I'm saying that he demonstrates why supporting
`..<` but not `<..` is not arbitrary or capricious. Dijkstra's
argument *permits* us to privilege `..<` as uniquely important, but
doesn't *force* us to do so.

I agree. And I still think it's uniquely important ;-).

To another person just now, you said:

He was talking about ranges of integer indices, though, and even
more-specifically about how to address arrays. Range<Bound> is a more
general concept that applies to much more than indices. Once you
involve floating point (and rationals, and patterns for matching,
e.g. UnicodeScalar("a")..."z"), the conclusions no longer apply.

I actually think he was talking a little more broadly than
that—essentially, he was discussing ordered, discrete types. In
principle, the same argument applies to UnicodeScalars,

Yes, but I don't know if he had such types.

but not to floating-point numbers (unless you use treat floats as a
discrete type using `nextafter` as the `successor()` operation, which
is coherent but not very useful in practice). Having said that, I *do*
think that `...` is in practice quite useful for many types. I'm less
certain that `<..` or `<.<` are.

* * *

By the way, another reason to have `stride` as a free function is that
I think some types need a "strider", an instance which performs the
striding.

That was the conclusion I came to when I started experimenting with
striding over NSDates a week or two ago. The best design I could come
up with looked like this:

  calendar.using(.Day).stride(from: startDate, to: endDate, by: 1)

The `start` and `end` parameters could be grouped together into a
single parameter to match `stride(over:by:)`, but you can't put the
calendar or the unit into the stride—without them, there is no
coherent way to calculate the distance between two dates.

So if some types need a strider, and will need to have the method
structured as `strider.stride(something:by:)`, it seems like the free
function version for types which *don't* need a strider ought to be
`stride(something:by:)`. The `something.striding(by:)` design can't be
easily adapted to this situation.

  calendar[startDate..<endDate].striding(by: .Day)

?

···

on Wed Apr 06 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

--
Dave

Precisely because the free function is also range-free, the sign of the step need not repeat the direction intent which is already indicated with `from: 200, to: 0`. In other words, I think this is much more intuitive:

     `stride(from: 200, to: 0, by: 2)`

milos

···

On 6 Apr 2016, at 18:57, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Meanwhile, I was trying to talk about something like `stride(from: 200, to: 0, by: -2)`, which is easily expressed today but isn't straightforward at all to preserve with only ranges.