It's much too late for Seq.apply (a method I'd much rather get rid of
entirely to be honest: I am completely convinced it should only be on
IndexedSeq) and I was wontfixed on negative indices even in a narrow
context with specific motivation.
https://issues.scala-lang.org/browse/SI-4500
"splitAt should work with negative indices"
I agree with you. It is probably too late to change apply(), but
perhaps a new method could be created, such as .at(-1)?
Cheers,
Greg
It is unfortunate that it is unbecoming to say things like "whoosh" or
make references to the missing the point olympics. If today were
opposite day and those things were ununbecoming, then today would be
looking pretty good.
> def at(index: Int): A =
> if(index >= 0)
> apply(index)
> else
> apply(size()-1+index)
With the minor detail that it changes the semantics of every piece of
scala code on the planet.
How exactly does adding a new method with a brand new name intentionally
chosen to *not* conflict with any existing method name change the
semantics of every piece of scala code on the planet?
Methinks I am not the one who has had a "whoosh" moment here...
Cheers,
Greg
I'll split it with you. You are still whooshing on the fact that the
implementation in isolation represents a negligible fraction of the
total cost to be borne. But being a fiery competitor, I wasn't going to
let that keep me off the track in today's olympic trials.
On 5/18/11 1:17 PM, Paul Phillips wrote:How exactly does adding a new method with a brand new name intentionally chosen to *not* conflict with any existing method name change the semantics of every piece of scala code on the planet?
It is unfortunate that it is unbecoming to say things like "whoosh" or
make references to the missing the point olympics. If today were
opposite day and those things were ununbecoming, then today would be
looking pretty good.
> def at(index: Int): A =With the minor detail that it changes the semantics of every piece of
> if(index>= 0)
> apply(index)
> else
> apply(size()-1+index)
scala code on the planet.
I have a suggestion. Rather than just saying "whoosh!" and alienating a
newcomer (namely myself) to this list who was genuinely curious and
looking forward to an explanation as to exactly what he is missing that
would make it so difficult to implement this method beyond his proposal,
why don't you actually explain what it is that you are getting at?
I am sufficiently familiar with programming and programming languages in
general to conceive of the possibility of cross-cutting concerns, but I
am not familiar enough with the Scala collection classes in particular
to be able to see what it is that I am missing that would make such a
seemingly simple addition to them as monstrously complicated as you all
seem to be making it out to be.
Cheers,
Greg
Oh wait, my brain was fogged and I see now that I did not fully process
your message before responding to it. :-) I was actually thinking in
terms of what would happen if someone had already created an "at" method
in their subclass with completely different behavior, but that wasn't
what you were getting at. Your point was that introducing an .at()
method requires that all subclasses obey the contract that the semantics
of .at() be conserved and thus they must override it if necessary in
order to make their interface have consistent behavior. This actually
is a point that I hadn't considered --- it is essentially the same point
I had in mind that changing a superclass changes the semantics of its
subclasses, but it is a different variation on this theme that I hadn't
considered --- so I appreciate you taking the time to mention it to me,
though it is hard to see how .at() could ever cause problems this way in
practice.
Cheers,
Greg
Who cares? Littering the library with method names with superset
functionality of other methods is a horrid idea. There's nothing in
the name "at" that suggests that its functionality is a superset of
"apply". How will you explain this to your grandchildren?
> It seems to me
> (probably naively) that all it would take to implement it would be a single
> addition to the scala.collection.Seq trait which would look something like:
>
> def at(index: Int): A =
> if(index >= 0)
> apply(index)
> else
> apply(size()-1+index)
You do realize that, if size() is O(N), that this is a grossly
inefficient implementation, don't you? That there's an implementation
that only requires one traversal?
-- Jim
P.S. You arithmetic is wrong.
Nothing, but that kind of argument can be used against every convenience
library function...
Cheers,
Greg
i don't see how your wrapped index is useful in general scenarios. i could equally argue that it should be clipped:
def at( idx: Int ) : T = apply( math.max( 0, math.min( size - 1, idx ))
and from looking at my code base i would argue that this case is much more likely than wanting the index to wrap.
best, -sciss-
Really? Python has a stack of collection classes with varying
implementations and big-O performance? Or do you mean that Python's
"lists", actually vectors, with O(1) indexing and length, provide
this convenience?
-- Jim
Which methods currently in the library save the typing of 8
characters? And to both of you: seq(seq.size - 1) takes twice as long
as the efficient implementation for sequences where size is O(N). The
equivalence holds in Python for its vectors; it does not hold
generally for Seqs ... and libraries should have efficient
implementations, especially for usages so common that they warrant
"convenience".
-- Jim
Sheesh, I subscribe to many programming language lists, and I have never
seen such casual hostility like I have on this one...
Yes, clearly that is what he meant. Ergo, charitably, if such a feature
existed we would probably want it to be a feature only of IndexedSeq and
not of Seq in general.
Cheers,
Greg
Grr, unfortunately I can't think of examples off the top of my head, but
essentially there are times in algorithms where you are subtracting
numbers from indices and it is useful for them to just wrap around. So
a generalization of this feature is really not just accepting negative
indices but to take the index modulo the size.
It's one of those features that you miss when you don't have it but that
when you wonder how you could have lived without it when you do. :-)
> convenient is also to have mean, median, variance, standard deviation, histogram, choose (get me a random element), etc., and still they are not in the standard interface.
>
True, my point is just saying that the fact that you could accomplish
something by other means doesn't mean that it therefore doesn't belong
outside the library, but in retrospect I see that the grandparent post
was really saying something more along the lines that the benefits of
seq(-1) are not nearly strong enough to merit being a library function
so my reply was not a particularly good response and therefore I
actually officially retract it. :-)
> i don't see how your wrapped index is useful in general scenarios. i could equally argue that it should be clipped:
>
> def at( idx: Int ) : T = apply( math.max( 0, math.min( size - 1, idx ))
>
> and from looking at my code base i would argue that this case is much more likely than wanting the index to wrap.
>
> best, -sciss-
Hmm, that is a good point. In that case, I propose that *both* exist!
Thank you for replying in an insightful and non-hostile manner. :-)
Cheers,
Greg
I'm not following this. Seq#apply has a contract, "returns the element
of this sequence at index idx, where 0 indicates the first element",
that any subclass must obey. Given that, and discounting his buggy
arithmetic, Gregory's naive implementation should yield the right
result for any subclass of Seq.
-- Jim
Given the remarkable hostility in your own comment, I simply do not believe you.
> Yes, clearly that is what he meant.
WHOOSH!
> Ergo, charitably, if such a feature
> existed we would probably want it to be a feature only of IndexedSeq and not
> of Seq in general.
Who would want that? Not anyone sensible who recognizes the value of
consistency.
-- Jim
Which methods currently in the library save the typing of 8On Wed, May 18, 2011 at 2:35 PM, Gregory Crosswhite
<gcros...@gmail.com> wrote:
> On 5/18/11 1:21 PM, Sciss wrote:
>>
>> also, what's so hard about writing seq(seq.size - 1) if you need to access
>> the last element of an indexed sequence? i find seq(-1) quite esoteric to be
>> part of and useful in a standard collections library.
>
> Nothing, but that kind of argument can be used against every convenience
> library function...
characters?
at -- plain lookup; syntactic sugar is square brackets, e.g. seq[1]
clipAt -- clips index to 0 ... size-1
wrapAt -- does a module size to the index
foldAt -- does a ping-pong (e.g. an index of size gets mapped to size - 1, an index of size - 1 gets mapped to size - 2 etc.)
:)
best, -sciss-
Okay, you all win. Clearly I am the one who does not belong on this
list. Have fun!
- Greg
BTW: SuperCollider's collection classes have four methods:
at -- plain lookup; syntactic sugar is square brackets, e.g. seq[1]
clipAt -- clips index to 0 ... size-1
wrapAt -- does a modulo-size to the index
foldAt -- does a ping-pong (e.g. an index of size gets mapped to size - 1, an index of size + 1 gets mapped to size - 2 etc.)
:)
best, -sciss-
To clarify: it would be a (nother) horrid idea for apply() or at() to
take negative arguments only for IndexedSeq and not for Seq. It would
not be a horrid idea for apply to be a method of IndexedSeq but not
Seq, as Paul urged (for another universe where Seq#apply doesn't yet
exist).
-- Jim
Rex, this is one of the many times when you have replied to me that I
have felt inclined to say "whoosh!". Most of these features are not
there *only* to save a few characters, just because you can come up
with examples where they do.
-- Jim
Jim> P.S. Your arithmetic is wrong.
Which ironically kind of supports his thesis, actually. Since it shows
that you can never trust yourself, or anyone else, to get index
arithmetic right so we'd better provide safer methods :-)
However:
The negative index thing seems prone to off-by-one-errors to me, since
the left end is 0-indexed but the right end must be 1-indexed since
there's no negative zero (not as an Int, anyway).
Also, if I use an index that's too large, does that wrap around, too?
Plus it violates the fail-fast principle. If I accidentally use a
negative index I'd like an error, please, and right away!
Basically the wraparound thing is superficially appealing but actually
pretty iffy and problematic. Just like the rest of Python :-)
Anyway, we have reverse and takeRight and many other methods, so there's
plenty of other amusing possibilities not involving index arithmetic,
e.g.
scala> (1 to 10).takeRight(2).head
res2: Int = 9
scala> ((1 to 10).reverse)(2)
res3: Int = 8
Gregory: in time, you may come to find the "casual hostility"
invigorating. We don't mean it, except Jim Balter who does.
--
Seth Tisue | Northwestern University | http://tisue.net
lead developer, NetLogo: http://ccl.northwestern.edu/netlogo/
class RichIndexedSeq[ T ]( xs: IndexedSeq[ T ]) {
def wrapAt( idx: Int ) : T = {
val sz = xs.size
if( sz == 0 ) throw new IndexOutOfBoundsException()
val i = if( idx >= 0 ) idx % sz else (sz - 1) - ((-1 - idx) % sz)
xs( i )
}
def clipAt( idx: Int ) : T = xs( math.max( 0, math.min( xs.size - 1, idx )))
def bounceAt( idx: Int ) : T = {
val sz = xs.size
if( sz == 0 ) throw new IndexOutOfBoundsException()
val sz2 = (sz - 1) << 1
val i = if( idx >= 0 ) idx % sz2 else (sz2 - 1) - ((-1 - idx) % sz2)
val j = if( i < sz ) i else sz2 - i
xs( j )
}
}
implicit def enrichIndexedSeq[ T ]( xs: IndexedSeq[ T ]) = new RichIndexedSeq[ T ]( xs )
val x = Vector(0,1,2,3)
val i = (-10 to 10)
i.map( x.wrapAt( _ ))
i.map( x.clipAt( _ ))
i.map( x.bounceAt( _ ))
best, -sciss-
word.
Since there's no smiley, you seem to be seriously making some claim,
but I can't make out exactly what it is. I can tell you that you know
a lot less about me than you think you do.
-- Jim
At would be more efficient for many collections, more compact to write, and it's clearer what's going on once you know the notation. All slightly nice, at the cost of library complexity and maintenance.
There really isn't any difference here between at as a nicety method and a bunch of other nicety methods that have a modest advantage in clarity or efficiency or whatnot.
Personally, I think a lot of methods should be (eventually) removed, or better yet, moved from the core collection into "styles" that you could get from importing the right implicit conversions. But if you're arguing that "at" is of a fundamentally different nature than my examples, you should be more explicit about what you think the difference is.
--Rex
I want to make clear I wasn't implying changing any parts of the Scala library to support this--I hope that was clear from the title of my original post. I'd just come across a situation where this would have been useful, and would curious as to other people's thoughts and why it had not originally been done.
Thanks,
Ken
Ken
Python had a partial solution for slices (very imperfect, I admit). Doing
something like seq[-3:] would return the last three items of a sequence.
So "nothing" represented "-0" so to speak. The obvious problem was that
you couldn't pass in an index to represent that. But comparing that to
seq[-3:seq.length] (or whatever it was, my Python is pretty rusty right now)
did a small but measurable amount in reducing errors.
>
> Basically the wraparound thing is superficially appealing but actually
> pretty iffy and problematic. Just like the rest of Python :-)
>
I'm glad the smiley is there. Python is certainly not perfect (hey, I'm using Scala
now), but it was loads ahead of most of what else existed at the time. It's not
like I'm advocating Perl features.
On Wed, May 18, 2011 at 9:24 PM, Gregory Crosswhite <gcros...@gmail.com> wrote:
On 5/18/11 9:38 AM, Ken McDonald wrote:
Like, where, seq(-1) is the last element of the sequence, etc. Python does things this way, and it's a really nice convenience.
Ken
I agree with you. It is probably too late to change apply(), but perhaps a new method could be created, such as .at(-1)?
No. We are now at a point where we need to treat collections as more or less fixed. No new methods just for nicety's sake. Sometimes I envie Java for its imposed rigidity. Nobody proposes new methods for Java collections because the price to pay (re-implement all implementations) is obviously too high. In Scala, the costs of bloat and change are less obvious but nevertheless real.
Cheers
-- Martin
Indeed, that's a strong argument against that.
While I find convenient to have a single character notation for such usage, I find this
often confusing in the languages I use (eg. Lua).
And somehow, using a sign bit to express a different meaning on the parameters feels
low-level, like an ASM or C trick, from a world where every bit counts in a 4KB memory and
a 1MHz processor (the kind I used when I started to program...).
Somehow, perhaps a better, more modern API could look like:
val v = seq(0, fromEnd = true) // Defaulting to false
One can argue against boolean parameters with default value (verbose, used only when
meaning the opposite of default, etc.) but at least, it is more readable that seq(0, true)
we can find in Java code...
--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --
For that matter, I like that feature as well, and, if Scala had it
default like Python, I'd have spent much more time last Saturday
figuring out why my code wasn't working, when it was just a matter of
off-by-1 errors.
--
Daniel C. Sobral
I travel to the future all the time.
Disclaimer: I am not advocating adding this feature to Scala.
That said, I like this feature in Python and I don't know what this
example proves. The code is equally valid (invalid?) if you use
"asWords(s.length)" which breaks currently *and* under the new
proposal... it has one broken case and 4 cases that don't break, just
like the original example. Given that the example can only work for 4
inputs, and breaks on all other inputs, it is pretty contrived.
I can imagine someone making this argument to show that people
shouldn't be using sequence indices, but should be using folds and
writing recursive functions. I don't see how it addresses the negative
indexing feature itself.
There is no shortage of rope in Scala, and the feature was not rejected
on the basis of its semantics, but rather because of the burden of
maintenance, documentation, and testing.
I find this feature very useful in languages where it is baked in from
the beginning (e.g. Python). I am not sure I'd find this feature useful
in a language where it was added on as an afterthought (e.g. Scala).
-- Erik
On Wed, May 18, 2011 at 19:11, Bill Venners <bi...@artima.com> wrote:For that matter, I like that feature as well, and, if Scala had it
> Hi All,
>
> I'm not sure what WHOOSH means yet, but I just wanted to point out that
> anyone who likes that at method that takes a negative index can have their
> cake and eat it too with an implicit conversion. I actually quite liked that
> feature of Python myself, but it never occurred to me or felt like it was
> "missing" in Scala.
default like Python, I'd have spent much more time last Saturday
figuring out why my code wasn't working, when it was just a matter of
off-by-1 errors.
That said, I like this feature in Python and I don't know what thisexample proves. The code is equally valid (invalid?) if you use
"asWords(s.length)" which breaks currently *and* under the new
proposal... it has one broken case and 4 cases that don't break, just
like the original example. Given that the example can only work for 4
inputs, and breaks on all other inputs, it is pretty contrived.
-- Erik
>> One could think of a class inheriting the Seq trait where apply is
>> overriden. Therefore an access by .at(-1) should also be overriden if a
>> particular semantics must be conserved. If the user of an API uses
>> .at() of
>> that SeqChild class, thinking it would produce the same result as a
>> regular
>> apply() access, he would run into problems.
> I'm not following this. Seq#apply has a contract, "returns the element
> of this sequence at index idx, where 0 indicates the first element",
> that any subclass must obey. Given that, and discounting his buggy
> arithmetic, Gregory's naive implementation should yield the right
> result for any subclass of Seq.
True - as long as it remains naive.
But then optimizations may be implemented, working directly with data
structure bypassing apply().
Say, in a couple of years, when everyone already forgotten this
discussion, and would be surprised by strange bugs.
---
And - already asked - why Seq ?
When u load a database table, it might be one-directional or
bi-directional.
Same way for sequences, fixed-size array (is it Python's Vector mentioned
above?) would be very easy and fast to implement at on top of apply.
For usual one-way List or Stack it would be not so efficient though.
So either use of .reverse() would be better or iterating all item of List
required.
But .reverse() is nice when u save (cache) the result, for at (-2),
at(-3), etc.
Should List cache it ? if yes, then when to dispose it ? What about
mutable List ?
Isolation vs optimisation, common trait :-D
If u implement negative indice ouside the class, in your code, you can
.reverse() the list and hold it as long as u need. But inside the List
class, how can the lib know what u try to do ?
I also think that non-effective method over List would be like provoking
to use it without thinking.
---
My guess, is that those wrapping, bouncing and clipping methods might be
nice for SOME sequences, with easy cheap access to any random item and
easy cheap access to current length.
Yet for Seq in general they would have to choose the implementation:
* either naive one, provoking slow ineffective coding
* or optimised complicated ones, including changing the internal data
structures as well not only code, and provoking sudden bugs in the future.
Maybe that would really be better made as an syntactic sugar - implicit
conversion lib for some carefully selected subclasses of Seq, but not the
Seq in general.
Or some trait, that only can be applied to "some carefully selected
subclasses of Seq", if this is possible.
--
Написано в почтовом клиенте браузера Opera: http://www.opera.com/mail/