This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Inconsistency of operators

60 replies
Derek Williams 3
Joined: 2011-08-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators
On Mon, Dec 5, 2011 at 12:35 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Mon, Dec 5, 2011 at 11:22, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
>
> Also, if you provide +: as an extractor for all collections, you add
> another inconsistency. Will you also provide :+? What about efficiency
> concerns?

I don't see why not. The efficiency of :+ as an extractor is exactly
the same as +: for IndexedSeq collections, and bad (or downright
impossible for infinite collections) for LinearSeq. So, perhaps, it
could be defined as accepting only IndexedSeq? Nevertheless, it is
efficient enough for many classes.

A Queue is a LinearSeq that could have an efficient append as well as prepend, although I'm not sure if the current immutable.Queue is implemented this way (if it isn't, it probably should).  --
Derek Williams
Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators

On Mon, Dec 5, 2011 at 5:22 AM, martin odersky wrote:
>> Ok, that's nice. In fact, it's nicer than with :: because it's so
>> obvious that the association is right to left.
>>
>
> It would also invalidate every single document and book that was ever
> written about Scala. Sometimes we have to live with history. :: came
> from ML, is close to Haskell's :. We can't simply replace it with a
> new operator. Imagine Haskell programmers proposed to change : to +:,
> maybe for the same reasons? Would you think that was viable?

Having an asymmetric operator for an asymmetric operation seems like
such a good idea to me. I always viewed Haskell programmers as being
mentally flexible and open to better ideas, so I imagine they'll
embrace it at once :-)

> Also, if you provide +: as an extractor for all collections, you add
> another inconsistency. Will you also provide :+? What about efficiency
> concerns?

No, I had thought of providing an +: extractor only for lists. It
doesn't make sense to destructure an arbitrary sequence.

> The way I see it is much closer to the status quo.
>
> For sequences:
>
> 1) Append is :+, prepend is +:, remove is -
> 2) Bulk ops are ++, ++:, --
> 3) Mutators are +=, ++=, +=: ++=: -= --=
>
> Yes, there's a bias for bulk ops vs single ops. I think that's OK
> because it makes bulk ops simpler and emphasizes the fact that bulk
> ops are supposed to be associative.
>
> For lists: In addition can compose and pattern match with ::.
>
> For streams: In addition can compose and pattern match with #::
>
> For sets and maps: Append/prepend ops are replaced by +. Only unbiased
> mutators +=, ++=, -= exist.
>
> I think that's not so bad!

Actually, that is the status quo. No, it's not so bad. But it could be
more consistent :-)

If consistency isn't as important as convenience and tradition, I'd
suggest deprecating ++: +=: ++=: :::. These four seem way more trouble
than they are worth.

Anyway, here is how I'll describe it:

Append is :+, prepend is +: (except for lists it's :: and for streams #::)
Add is + (but be careful with strings), remove is -
Bulk add/append is ++, bulk remove is --
Mutations are += ++= -= --=
Stay away from ++: +=: ++=: :+= :::

Cheers,

Cay

Richard Wallace
Joined: 2009-07-14,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators

On Sat, Dec 3, 2011 at 12:46 PM, Kenneth McDonald wrote:
>> My gripe is with the operators to add and remove items. They are 17
>> operators + ++ :+ +: ++: :: ::: (for lists) | (for sets) += ++= +=:
>> ++=: (for buffers) - -- &~ (for sets) -= --=
>
> These are painful and do not contribute to readable code. I would suggest
> the following conventions.
> +, -  -- obvious

Funny you say that, because the current use of + for adding a single
element is actually incorrect according to the obvious implementation.
The reasoning goes as thus

The most universally known definition of '+' is from mathematics.
It's used for "adding" real numbers together - although it is first
introduced as adding natural numbers together. This is practically
the first thing we all learn in elementary school.

In elementary computer programming classes we all learn that computers
are actually pretty bad at math. Specifically, they can't really
represent real numbers accurately. So we break the '+' function up to
work with different types, namely: Short => Short => Short, Int => Int
=> Int, Long => Long => Long, Float => Float => Float, Double =>
Double => Double.

We also tend to deal with String concatenation quite a bit, so we also
define the '+' function for strings, String => String => String.

At this point we see a pretty obvious pattern emerging for how we can
generalize this function '+' :: A => A => A.

So, logically if we were to define '+' for Lists and Sets we should
have the types being List[Z] => List[Z] => List[Z] and Set[Z] =>
Set[Z] => Set[Z]. The implementations are obvious - concatenation and
union - and the Z type can be any type because it plays no role in the
implementation.

We can even define '+' for Maps. It's simply a merging of two maps,
with the requirement that the value type also have '+' defined.

At some point in time someone with a better knowledge of math will
come along and say, "Hey, I recognize that! It's called a
Semigroup[1] and if you have a Semigroup and a zero value, you have a
Monoid[2]."

It would be wonderful if we could get these and other really really
useful, simple concepts in the standard Scala library.

[1] http://en.wikipedia.org/wiki/Semigroup
[2] http://en.wikipedia.org/wiki/Monoid

> ++, --  --takes a bit of learning, but obvious once one realizes they
> generalize + and - so that the rhs is a collection, not a single element.
> +=, -=, ++-, --=  --obvious generalizations of previous four operators.
> ...and anything else should be a named method ('cause it's not even _close_
> to obvious).
> Ken
>

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Inconsistency of operators


Am Montag, 5. Dezember 2011 22:05:03 UTC+1 schrieb Cay Horstmann:
On Mon, Dec 5, 2011 at 5:22 AM, martin odersky <martin [dot] [dot] [dot] [dot] [at] epfl [dot] ch> wrote:> Also, if you provide +: as an extractor for all collections, you add

> another inconsistency. Will you also provide :+? What about efficiency
> concerns?

No, I had thought of providing an +: extractor only for lists. It
doesn't make sense to destructure an arbitrary sequence.



In that case please consider that :: is actually a case class, a fact that is intimately coupled with why lists are special wrt. deconstructing them. If you provide an extractor, it will run slower, but that will not be obvious to anyone not looking at the implementation (because extractors—unlike case classes—need to construct Some((x, xs))s for signaling success).

So, by saying that it does not make sense to have this extractor on anything but List, you basically say that either:

- :: should stay the only choice, because +: would be suboptimal

or

- case class :: should be renamed to +:, which for obvious reasons is highly unlikely (as it would break virtually every Scala program in existence)

Regards,

Roland
 

> The way I see it is much closer to the status quo.
>
> For sequences:
>
> 1) Append is :+, prepend is +:, remove is -
> 2) Bulk ops are ++, ++:, --
> 3) Mutators are +=, ++=, +=: ++=: -= --=
>
> Yes, there's a bias for bulk ops vs single ops. I think that's OK
> because it makes bulk ops simpler and emphasizes the fact that bulk
> ops are supposed to be associative.
>
> For lists: In addition can compose and pattern match with ::.
>
> For streams: In addition can compose and pattern match with #::
>
> For sets and maps: Append/prepend ops are replaced by +. Only unbiased
> mutators +=, ++=, -= exist.
>
> I think that's not so bad!

Actually, that is the status quo. No, it's not so bad. But it could be
more consistent :-)

If consistency isn't as important as convenience and tradition, I'd
suggest deprecating ++: +=: ++=: :::. These four seem way more trouble
than they are worth.

Anyway, here is how I'll describe it:

Append is :+, prepend is +: (except for lists it's :: and for streams #::)
Add is + (but be careful with strings), remove is -
Bulk add/append is ++, bulk remove is --
Mutations are += ++= -= --=
Stay away from ++: +=: ++=: :+= :::

Cheers,

Cay

etorreborre 2
Joined: 2011-02-23,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators
Hi Cay,

Anyway, here is how I'll describe it:

Append is :+, prepend is +: (except for lists it's :: and for streams #::)
Add is + (but be careful with strings), remove is -
Bulk add/append is ++, bulk remove is --
Mutations are += ++= -= --=
Stay away from ++: +=: ++=: :+= :::


That's how I've been using these operators and that fits pretty well in my small brain.
Eric.
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: Inconsistency of operators

On Mon, Dec 5, 2011 at 19:05, Cay Horstmann wrote:
>> Also, if you provide +: as an extractor for all collections, you add
>> another inconsistency. Will you also provide :+? What about efficiency
>> concerns?
>
> No, I had thought of providing an +: extractor only for lists. It
> doesn't make sense to destructure an arbitrary sequence.

That's non-sense! If one has to dump List because it sucks for any
algorithm that's not stack-based, it doesn't mean getting heads of a
sequence stop being useful. In fact, it is expanded by getting lasts
as well. And that doesn't even consider the mutable data structures
that would benefit from it.

> If consistency isn't as important as convenience and tradition, I'd
> suggest deprecating ++: +=: ++=: :::. These four seem way more trouble
> than they are worth.

And this is even weirder: you just killed mutable stacks. Plus removed
::: *and* the only possible right-associative replacement at the same
time.

I see a lot of cost and very little gain from what you propose.

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators

On Tue, Dec 6, 2011 at 6:03 AM, Daniel Sobral wrote:
> On Mon, Dec 5, 2011 at 19:05, Cay Horstmann wrote:
>>> Also, if you provide +: as an extractor for all collections, you add
>>> another inconsistency. Will you also provide :+? What about efficiency
>>> concerns?
>>
>> No, I had thought of providing an +: extractor only for lists. It
>> doesn't make sense to destructure an arbitrary sequence.
>
> That's non-sense! If one has to dump List because it sucks for any
> algorithm that's not stack-based, it doesn't mean getting heads of a
> sequence stop being useful. In fact, it is expanded by getting lasts
> as well. And that doesn't even consider the mutable data structures
> that would benefit from it.

Nobody wants to "dump List". I merely proposed adding a +: extractor
so that one could, over time, migrate from the :: operator to the more
consistent +: operator.

Right now, we have :: destructuring for lists only. so I don't see
that it would do any harm defining a +: extractor for lists only.

Anyway, it's a moot issue. The :: operator for lists seems to be
entrenched. If :: isn't going to go away, I don't want +: as a
gratuitous alternative.

>
>> If consistency isn't as important as convenience and tradition, I'd
>> suggest deprecating ++: +=: ++=: :::. These four seem way more trouble
>> than they are worth.
>
> And this is even weirder: you just killed mutable stacks.

It is interesting that you bring up mutable stacks... Check out the
API. You can't currently mutate a stack with +=. More inconsistency.
Nor can you use +=: or ++=:, so taking these away can't have any
effect on stacks.

Anyway, there are push and pop :-)

> Plus removed
> ::: *and* the only possible right-associative replacement at the same
> time.
>
> I see a lot of cost and very little gain from what you propose.
>

All I suggest is to remove those four operators. I looked at the
language and compiler source. ++: +=: ++=: are very rarely used. And
it seems that ::: could be replaced by ++ in all the uses that I've
analyzed. Given that Martin greatly preferred ++ over the more
consistent :++ because it is shorter, I feel confident that he'll
prefer ++ over ::: for the same reason :-)

Seriously, I don't think I am going to get my way with :::, but
deprecating ++: +=: ++=: is worth fighting for.

Cheers,

Cay

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: Inconsistency of operators

On Tue, Dec 6, 2011 at 17:26, Cay Horstmann wrote:
>>> No, I had thought of providing an +: extractor only for lists. It
>>> doesn't make sense to destructure an arbitrary sequence.
>>
>> That's non-sense! If one has to dump List because it sucks for any
>> algorithm that's not stack-based, it doesn't mean getting heads of a
>> sequence stop being useful. In fact, it is expanded by getting lasts
>> as well. And that doesn't even consider the mutable data structures
>> that would benefit from it.
>
> Nobody wants to "dump List". I merely proposed adding a +: extractor
> so that one could, over time, migrate from the :: operator to the more
> consistent +: operator.

You missed my point. *I* am dumping List in favor of Vector or
something else. Or, rather, when I do that, I still want a nice
extractor for the sequence.

> All I suggest is to remove those four operators. I looked at the
> language and compiler source. ++: +=: ++=: are very rarely used. And
> it seems that ::: could be replaced by ++ in all the uses that I've
> analyzed. Given that Martin greatly preferred ++ over the more
> consistent :++ because it is shorter, I feel confident that he'll
> prefer ++ over ::: for the same reason :-)
>
> Seriously, I don't think I am going to get my way with :::, but
> deprecating ++: +=: ++=: is worth fighting for.

Well, +=: is enqueue, ++=: is enqueueAll. There's much the compiler
doesn't use, which doesn't mean it isn't useful. And both ++: and :::
are right-associative, which ++ isn't.

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators

On Tue, Dec 6, 2011 at 12:00 PM, Daniel Sobral wrote:
> On Tue, Dec 6, 2011 at 17:26, Cay Horstmann wrote:
>> Nobody wants to "dump List". I merely proposed adding a +: extractor
>> so that one could, over time, migrate from the :: operator to the more
>> consistent +: operator.
>
> You missed my point. *I* am dumping List in favor of Vector or
> something else. Or, rather, when I do that, I still want a nice
> extractor for the sequence.
>

I see. Personally, I haven't been in the habit of taking vectors apart
into head and tail, but if you want to lead the fight for more
extractors, go for it. Just make them consistent with the operators
:-)

>> Seriously, I don't think I am going to get my way with :::, but
>> deprecating ++: +=: ++=: is worth fighting for.
>
> Well, +=: is enqueue, ++=: is enqueueAll.

No. += is enqueue.

val q = new scala.collection.mutable.Queue[Int]
q.enqueue(1) // Queue(1)
q += 2 // Queue(1, 2)
3 +=: q // Queue(3, 1, 2)

> And both ++: and :::
> are right-associative, which ++ isn't.

That is true. How often do you care? Enough to pay for the cognitive
burden of an inconsistent set of operators? You can just use ++ and ()
when needed.

Cheers,

Cay

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Inconsistency of operators

On Mon, Dec 5, 2011 at 5:22 AM, martin odersky wrote:
> 3) Mutators are +=, ++=, +=: ++=: -= --=

I don't want to pour salt in the wound. Ok, I do.

It's not nice that +=: and ++=: have the = in the middle. If they had
it it in the beginning, the compiler wouldn't be willing to synthesize
+=:= and ++=:=.

scala> var a = collection.mutable.ArrayBuffer(1, 2)
a: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2)

scala> 3 +=: a
res5: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 1, 2)

scala> a +=:= 4

scala> a
res7: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(4, 3, 1, 2)

As an exercise to the reader, figure out the following, without using the REPL.

a ++=:= a ++: a ++=: a

Cheers,

Cay

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland