Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

any2stringadd implicit should be removed from Predef #194

Closed
scabug opened this issue Oct 19, 2007 · 30 comments
Closed

any2stringadd implicit should be removed from Predef #194

scabug opened this issue Oct 19, 2007 · 30 comments
Assignees
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Oct 19, 2007

This implicit removes too much type safety, and can lead to confusing results.

I think this is more important than the '+' operator being symmetric.

UPDATE 2015-07-29:
Workaround: You can opt-out on a per-file basis by unimporting it.

import Predef.{any2stringadd => _,_}
object foo extends App{
  println(new java.util.Date + " sfsdf") // error: value + is not a member of java.util.Date
}
@scabug
Copy link
Author

scabug commented Oct 19, 2007

Imported From: https://issues.scala-lang.org/browse/SI-194?orig=1
Reporter: @propensive
See #6806, #1931

@scabug
Copy link
Author

scabug commented Oct 19, 2007

@odersky said:
I disagree on this point. I believe `+' should work as in Java. For a long time we did not have any2stringadd and it was a constant pain, in particular for programmers recently converting from Java.

@scabug
Copy link
Author

scabug commented Jan 14, 2009

@odersky said:
Milestone 2.6.1 deleted

@scabug
Copy link
Author

scabug commented Nov 5, 2011

Eugene Yokota (eed3si9n) said:
Having continuity with Java is great, but not at the price of compromising type safety.
A good example is the use of null vs Option[A].
As a programmer get situated in Scala comfortably, he or she no longer thinks in terms of Java or other languages,
and a normal Scala would almost never use null actively (with a few exception in standard lib such as regex extraction and XML namespace).
In my opinion, having a source-level compatibility of + with Java doesn't buy much, and is very harmful to Scala as a language. More than once I've been bitten by unintended "Some(...)" or "None" showing up in string output.
An ideal course of action would be to deprecate any2stringadd so the compiler would start raising warnings, if not an optional compiler error would be helpful like -no-unsafeadd.

@scabug
Copy link
Author

scabug commented Apr 3, 2013

Heikki Vesalainen (hvesalai) said:
There should at least be a way to turn this implicit off.

@scabug
Copy link
Author

scabug commented Aug 9, 2013

@SethTisue said:
Now that Scala has string interpolation, there is less need to build strings with +. So perhaps this decision could be revisited.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

@Sciss said:
Constant pain point for DSLs and confusing many people. E.g. http://stackoverflow.com/questions/20800102/on-a-vector-gives-strange-wrong-type-errors . Should be re-opened I think.

@scabug
Copy link
Author

scabug commented Jan 27, 2014

Kevin Wright (kev.lee.wright) said:
+1 for re-opening

@scabug
Copy link
Author

scabug commented Jan 28, 2014

Heikki Vesalainen (hvesalai) said:
This issue should really be re-evaluated and discussed properly.

@scabug
Copy link
Author

scabug commented Jan 28, 2014

@soc said:
We should at least consider a standardized way for people to opt-out of this madness via a language import.

@scabug
Copy link
Author

scabug commented Jan 28, 2014

Kevin Wright (kev.lee.wright) said:
Is it worth considering a language import for Java source-compatibility in general?
No doubt there are other language features added for familiarity to Java developers, which now look "quaint" in retrospect.

@scabug
Copy link
Author

scabug commented Jan 28, 2014

Rich Oliver (rich oliver) said:
I actually find this can be particularly confusing when teaching complete beginners and introducing basic types, not to mention the weird errors about strings that appear out of nowhere when you have been near a string. 2 + "two" is wrong. It certainly shouldn't equal "2two".

Println etc can now be implemented as macros for ease of use. So there's even less justification for polluting the whole of the global name-space.

@scabug
Copy link
Author

scabug commented Jan 28, 2014

Alexandru Nedelcu (alex_ndc) said:
I think it's worth pointing out that this behavior is not common in many mainstream languages.

Here's Python:

>>> "" + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

Here's Ruby:

irb(main):001:0> "" + 2
TypeError: can't convert Fixnum into String
	from (irb):1:in `+'
	from (irb):1
	from /usr/bin/irb:12:in `<main>'

And in languages where it is, it's given as a bad example. I think Java folks can learn to appreciate a lack of implicit conversion to string, especially given that they don't need it with string interpolation.

@scabug
Copy link
Author

scabug commented Jan 29, 2014

Matthew Pocock (drdozer) said:
I feel that the rational for this ease of use is now much lessened by string interpolation. It is very easy to write f"$a $b" and it's crystal clear that we're doing stringy things so a+b as a shorthand now feels redundant.

What else needs tackling related to this? Perhaps we need to grep the stdlib for all def + (_: String): String declarations? For binary compatibility, can they be annotated @bridge for one release cycle, or is that for something else?

@scabug
Copy link
Author

scabug commented Jan 29, 2014

@adriaanm said:
Here's a crazy idea: how about deprecating any2stringadd, and making implicit search ignore deprecated implicits under -Xfuture.

@scabug
Copy link
Author

scabug commented Feb 12, 2014

@SethTisue said (edited on Feb 12, 2014 4:24:44 PM UTC):
I hope it's clear to everyone that the issue isn't just a theoretical one about rigor, that matters only if you have your head in the clouds. This is something that bites all Scala coders trying to write correct code, but that includes biting beginners all the time, when they are learning the language. So for example just now on IRC:

D: to append to a collection use :+ not +
I: ah, thanks!
I: I was following red herrings it seems
B: huh
B: required: String
I: yeah, that was what my reaction was too B. When I started looking at the documentation
  I figured the addString methods might be related, and the byte was getting converted to a Char implicitly,
  and then wanted to treat the whole thing as a String. But apparently it's something else entirely
B: ! Vector(new Foo, new Foo) + (new Foo)
multibot_:  error: type mismatch;
multibot_:   found  : Foo
multibot_:   required: String
B: not even "oh hey I think you might have meant String here", it's "REQUIRED String", wtf

This is just one example; I've seen versions of this exchange on #scala countless times over the years.

It gets especially confusing if someone points out or realizes that Set actually does expect + and not :+ or +: .

@scabug
Copy link
Author

scabug commented Feb 13, 2014

@SethTisue said:
for the record, there is a long discussion on this at https://groups.google.com/d/msg/scala-user/Mbtp_Cq6zKI/XZAdRftpdw8J , with Martin himself weighing in ("I can pretty much guarantee that a solution will come in due time. But I am also strongly proposed to any half-baked, temporary measures until then. These are well intended, to be sure, but in the end only lead to needless complexity. Give it time, and we will fix it for good")

@scabug
Copy link
Author

scabug commented Mar 18, 2014

Naftoli Gugenheim (naftoligug) said:
@seth - Martin's comment was not about a replacement for any2stringadd, but about a source-code rewriting tool. His comments in that thread about removing it are basically "we will break too much code."

@adriaan - nice idea, what would be arguments against it?

@scabug
Copy link
Author

scabug commented Mar 18, 2014

@SethTisue said:
@naftoli: But the purpose of the source-code rewriting tool is exactly to permit language changes like the removal of any2stringadd to happen (without causing too much pain for users).

@scabug
Copy link
Author

scabug commented Mar 18, 2014

@adriaanm said:
I'm planning support for customizing errors/warnings, adding external deprecations, and have implicit search ignore deprecated implicts,....
Too soon to promise anything, I'll bring it up on scala-internals when there's something to discuss :)

@scabug
Copy link
Author

scabug commented Mar 19, 2014

Naftoli Gugenheim (naftoligug) said:
@seth - Sure. Just wanted to clarify that, apparently my clarification wasn't too clear. :)

@scabug
Copy link
Author

scabug commented Jul 30, 2015

@cvogt said:
Since it hasn't been mentioned yet, I added the per-file opt-out workaround.

@scabug
Copy link
Author

scabug commented Jul 30, 2015

Lionel Parreaux (LPTK) said:
It's worth mentioning that the workaround only works when the import is done at the top level, which is not always possible (ie: in macro implementations). What I do is shadow the definition of Predef.any2stringadd by declaring a new one locally:

def any2stringadd = ???

@scabug
Copy link
Author

scabug commented Jul 30, 2015

@som-snytt said:

scala> case class C(c: String = "hi")
defined class C

scala> { def any2stringadd = ??? ; C() + "EOS" }
<console>:13: error: value + is not a member of C
       { def any2stringadd = ??? ; C() + "EOS" }
                                       ^

scala> { def any2stringadd = ??? ; (C(): any2stringadd[C]) + "EOS" }
res1: String = C(hi)EOS

scala> { def any2stringadd = ??? ; type any2stringadd[_] = Nothing ; (C(): any2stringadd[C]) + "EOS" }
<console>:13: error: type mismatch;
 found   : C
 required: any2stringadd[C]
    (which expands to)  Nothing
       { def any2stringadd = ??? ; type any2stringadd[_] = Nothing ; (C(): any2stringadd[C]) + "EOS" }
                                                                       ^
<console>:13: error: value + is not a member of any2stringadd[C]
       { def any2stringadd = ??? ; type any2stringadd[_] = Nothing ; (C(): any2stringadd[C]) + "EOS" }
                                                                                             ^

@scabug
Copy link
Author

scabug commented Jul 30, 2015

@puffnfresh said:
Another option is to make the implicit ambiguous, can do that just once for your project by putting it in a top-level package object:

package object io.atlassian {
  def undefined[A]: A = sys.error("undefined")

  // any2stringadd allows anything to be added against String.
  class AmbiguousStringAdd {
    def +(b: String) = undefined
  }

  implicit def amb1any2stringadd(a: Any) = new AmbiguousStringAdd 
  implicit def amb2any2stringadd(a: Any) = new AmbiguousStringAdd
}

@scabug
Copy link
Author

scabug commented Jul 30, 2015

@puffnfresh said (edited on Jul 30, 2015 10:20:02 PM UTC):
I also wrote a patch for #6806, which allows the above to have a nice error message:

scala/scala@4d4f703

I'll submit a PR to scala/scala

@scabug
Copy link
Author

scabug commented Sep 10, 2015

@SethTisue said:
latest go-round on scala-user: https://groups.google.com/d/msg/scala-user/9h41cxPDvI8/rYvCiX2hCgAJ

@scabug
Copy link
Author

scabug commented Aug 12, 2016

@SethTisue said:
proposal from @som-snytt: scala/scala#5235

@scabug scabug added this to the Backlog milestone Apr 6, 2017
@Jasper-M
Copy link
Member

Jasper-M commented Oct 1, 2017

Another newbie confused by any2stringadd.
While we're at it can we also remove the def +(any: Any): String from class String and replace it with def +(str: String): String? I guess that method is a compiler trick anyway so we can do with it whatever we want.

eed3si9n added a commit to eed3si9n/scala that referenced this issue Feb 8, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the convertion altogether under `-Xfuture` flag.

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
@SethTisue SethTisue modified the milestones: Backlog, 2.13.0-M4 Feb 17, 2018
@SethTisue SethTisue assigned eed3si9n and unassigned adriaanm Feb 17, 2018
eed3si9n added a commit to eed3si9n/scala that referenced this issue Feb 22, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

### Background

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

### Implementation note

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
eed3si9n added a commit to eed3si9n/scala that referenced this issue Mar 4, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

### Background

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

### Implementation note

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
eed3si9n added a commit to eed3si9n/scala that referenced this issue Mar 4, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

### Background

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

### Implementation note

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
eed3si9n added a commit to eed3si9n/scala that referenced this issue Apr 12, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

### Background

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

### Implementation note

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
eed3si9n added a commit to eed3si9n/scala that referenced this issue Apr 26, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

### Background

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

### Implementation note

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
@SethTisue SethTisue modified the milestones: 2.13.0-M4, 2.13.0-M5 May 9, 2018
eed3si9n added a commit to eed3si9n/scala that referenced this issue Jun 2, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
eed3si9n added a commit to eed3si9n/scala that referenced this issue Jun 2, 2018
Ref scala/bug#194

1. This deprecates scala.Predef.any2stringadd.
2. Another thing it does is to remove the conversion altogether under `-Xsource:2.14` flag.

Scala up till 2.12 has had two kinds of String concatenation operator `+` (also known as PLUS or ADD).

a. When the receiver is a non-String, it uses implicit `Predef.any2addstring` to inject `+`.
b. When the receiver is a String, the compiler injects a special method `String_+`.

In other words, `true + "what"` and `"what" + true` use different mechanisms.
This change addresses the first variant that introduces `+` operator into `Any`.
The rationale discussed in scala/bug#194 is that this implicit injection removes too much type safety, leading to confusing results.

Here some examples listed:

```
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()

scala> v += 3
<console>:13: error: value += is not a member of scala.collection.immutable.Vector[Int]
  Expression does not convert to assignment because:
    type mismatch;
     found   : Int(3)
     required: String
    expansion: v = v.$plus(3)
       v += 3
         ^

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs foreach { println _ + 1 }
<console>:13: error: type mismatch;
 found   : Int(1)
 required: String
       xs foreach { println _ + 1 }
                                ^
```

Note that in both examples the person who wrote the code were not trying to concatenate String.

Initially I've attempted to implement what Adriaan proposed in 2014 (scala/bug#194 (comment)) and remove all deprecated implicits under `-Xfuture` flag. A simple implementation

```scala
val fd = settings.future && sym.isDeprecated
```

did not work, because the implicit caching is unware of the annotations?
@SethTisue
Copy link
Member

scala/scala#6315, which deprecates any2stringadd, has been merged for 2.13.0-M5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants