Reputation: 876
Let's say I have the following piece of code:
object Potato extends App {
type CoolString = String
type AwesomeString = String
def potato(string: String)(implicit coolString: CoolString, awesomeString: AwesomeString) = {
s"$string is a string. Oh, but don't forget - $coolString and also $awesomeString"
}
implicit val ice : CoolString = "Really Cold Ice"
implicit val awe : AwesomeString = "Awe inspiring object"
potato("Stringerino")
}
This code fails with a problem
[error] ... ambiguous implicit values:
[error] both value ice in object Potato of type => Potato.CoolString
[error] and value awe in object Potato of type => Potato.AwesomeString
[error] match expected type Potato.CoolString
[error] potato("Stringerino")
Is such use of implicits impossible?
Upvotes: 3
Views: 451
Reputation: 149538
Is such use of implicits impossible?
Not only is it impossible, it is dangerous to rely on such a method taking in a type as general as String
as an implicit. Think about it, any String
instance in scope will be an eligible candidate to get passed into the method!
A type alias is just, well, a type alias, nothing more. To the compiler both CoolString
and AwesomeString
are merely String
.
A better approach is to leverage Tagged Types. For example, this is a tagged type using shapeless:
import shapeless.tag.@@
trait CoolString
trait AwesomeString
type ReallyCoolString = String @@ CoolString
type ReallyAwesomeString = String @@ AwesomeString
And then:
import shapeless.tag
def potato(string: String)(implicit coolString: ReallyCoolString, awesomeString: ReallyAwesomeString) = {
s"$string is a string. Oh, but don't forget - $coolString and also $awesomeString"
}
def main(args: Array[String]): Unit = {
implicit val ice = tag[CoolString][String]("Really Cold Ice")
implicit val awe = tag[AwesomeString][String]("Awe inspiring object")
println(potato("Stringerino"))
}
Yields:
Stringerino is a string.
Oh, but don't forget - Really Cold Ice and also Awe inspiring object
Upvotes: 6