The reason you get +
operator is working for you without using .toString
explicitly is described here: What Scala feature allows the plus operator to be used on Any?. Those additional implicits in Predef
are source of many problems in scala, but it's hard to get rid of such legacy.
To find out why addTwoThings("1",2)
is working - let's rewrite it to get exact inference for S
:
scala> def addTwoThings[S](item1:S, item2:S): S = item1
addTwoThings: [S](item1: S, item2: S)S
scala> addTwoThings(1, "1")
res5: Any = 1
You can notice that type S = Any
was inferred as a common type.
So, here are several solutions:
1) If you can allow two type-parameters in your method's signature, here is the solution:
def addTwoThings[S1, S2](item1:S1, item2:S2)(implicit ev: S1 =:= S2, ev2: S2 =:= S1) = {
item1 + " | " + item2
}
Note: ev2 might be redundant for check, but it provides more complete equality, see Scala: generic method using implicit evidence doesn't compile
Experiments:
scala> addTwoThings(1, "1")
<console>:18: error: Cannot prove that Int =:= String.
addTwoThings(1, "1")
^
scala> addTwoThings("2", "1")
res11: String = 2 | 1
2) Or you can exclude Any
/AnyRef
(common supertype for everything) using Evidence that types are not equal in Scala :
trait =:!=[A, B]
implicit def neq[A, B] : A =:!= B = new =:!=[A, B] {}
implicit def neqAmbig1[A] : A =:!= A = ???
implicit def neqAmbig2[A] : A =:!= A = ???
def addTwoThings[S](item1:S, item2:S)(implicit ev: S =:!= Any, ev2: S =:!= AnyRef): S = item1
Experiments:
scala> addTwoThings(1, "1")
<console>:18: error: ambiguous implicit values:
both method neqAmbig1 of type [A]=> =:!=[A,A]
and method neqAmbig2 of type [A]=> =:!=[A,A]
match expected type =:!=[Any,Any]
addTwoThings(1, "1")
^
scala> addTwoThings(1, 1)
res7: Int = 1
Note: This approach doesn't require exact type equality, so if B1 <: B2
- addTwoThings(b1, b2)
- will still work. It protects you only from unrelated type hierarchies (which might be useful). In practice != Any
without != AnyRef
will not give you error on object A; object B; addTwoThings(B, A)
.
Note2: The error provided by compiler is hardly readable, more info here - How can I customize Scala ambiguous implicit errors when using shapeless type inequalities
3) Another approach is currying:
def addTwoThings[S](item1:S)(item2:S) = ""
Experiments:
scala> addTwoThings(1)(1)
res8: String = ""
scala> addTwoThings(1)("1")
<console>:18: error: type mismatch;
found : String("1")
required: Int
addTwoThings(1)("1")
^
Type inference will not look for common supertype of curried parameters (you can read more here)