Reputation: 108
I mistakenly concatted a string with an Option[String] while coding in scala.
I expected as a strongly typed language, scala would not allow me to do such operation.
This is what I tried.
This works
scala> val a:String = "aaa"
val a: String = aaa
scala> val b:Option[String] = Some("bbbb")
val b: Option[String] = Some(bbbb)
scala> a + b
val res0: String = aaaSome(bbbb)
scala> val c:Option[String] = None
val c: Option[String] = None
scala> val d = a + c
val d: String = aaaNone
scala> val e = 1
val e: Int = 1
scala> a + e
val res2: String = aaa1
while this does not work
scala> val f:Option[String] = Some("ffff")
val f: Option[String] = Some(ffff)
scala> val g:Option[String] = None
val g: Option[String] = None
scala> f + g
^
error: type mismatch;
found : Option[String]
required: String
Why does scala allow such behavior? Dynamically typed languages like python will stop me from adding strings to int types, None types or any type other than strings. Curious if this design is intentional? If so why?
Upvotes: 3
Views: 289
Reputation: 524
Scala contains an implicit class any2stringadd
in it's Predef
package. This class is responsible for these concatenation operations.
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
What it means is that, by default, scope contains a method +
which can concatenate value of any type A
with string by converting value of this type to string via String.valueOf(...)
.
I can't speak of design choices, I agree that this might be an unexpected behavior. The same applies to Scala's native ==
method. For example, this code compiles just ok: Some("a") == "b"
. This can lead to nasty bugs in filtering and other methods.
If you want to eliminate this behavior I suggest you take a look at https://typelevel.org/cats/ library, which introduces different typeclasses that can solve this problem.
For example, for string concatenation you can use Semigroup typeclass (which has tons of other useful use-cases as well):
import cats.Semigroup
Semigroup[String].combine("a", "b") // works, returns "ab"
Semigroup[String].combine("a", Some("b")) // won't work, compilation error
This looks tedious, but there is a syntactic sugar:
import cats.implicits._
"a" |+| "b" // works, returns "ab"
"a" |+| Some("b") // won't work, compilation error
// |+| here is the same as Semigroup[String].combine
The same thing applies to ==
method. Instead you can use Eq
typeclass:
import cats.implicits._
"a" == Some("b") // works, no error, but could be unexpected
"a" === Some("b") // compilation error (Cats Eq)
"a" === "b" // works, as expected
"a" =!= "b" // same as != but type safe
Upvotes: 8