Suppose I am writing library code that should be easy to extend and to use without verbose syntax. It seems like implicit conversions can be used to avoid verbosity, as in the Scala Collections library, but I am struggling at applying it to traversables as follows.
I have a trait:
trait Wrapped[T]
{
def value : T
}
Then I have the class Foo
, a key class in the library. Foo
s are constructed with a list of anything that is Wrapped
.
case class Foo[T <: Wrapped[_]](lst : Traversable[T]) {
override def toString = lst mkString " "
}
A common use case would be wrapping an Int
so I provide a WrappedInt
class:
case class WrappedInt(value : Int) extends Wrapped[Int]
With this code, I can make a Foo
like this:
val wrappedInts = Seq(1, 2, 3, 4) map { new WrappedInt(_) }
val someFoo = Foo(wrappedInts)
I do not like the extra code to wrap here. I would like the following code to be equivalent:
val foo = Foo(Seq(1, 2, 3, 4)) //should be a Foo[WrappedInt] - gives error
I define the following implicit:
object Conversions {
implicit def intsToWrapped(lst : Traversable[Int]) = lst map { new WrappedInt(_) }
}
However, it still doesn't work, my val foo
has a compilation error after changing the Foo
constructor parameter to implicit lst
. The Scala book says that an implicit conversion essentially allows x + y
to be changed to convert(x) + y
, where convert
is an implicit conversion. It seems to me like I have the same exact case, one conversion of one parameter here is enough. I verify by doing this:
val foo = Foo(Conversions.intsToWrapped(Seq(1, 2, 3, 4)))
Why is my implicit not being applied? And is there a different, more idiomatic way in current Scala to let Foo
be constructed with less code?
EDIT: Adding import Conversions._
does not help and should, if I understand correctly, not be necessary because this example is in one file.
Specific compiler errors I get are these:
val foo = Foo(Seq(1, 2, 3, 4))
inferred type arguments [Int] do not conform to method apply's type parameter bounds [T <: Wrapped[_]]
type mismatch; found : Seq[Int] required: Traversable[T]
Specifying the type to help with type inference, like this:
val foo = Foo[WrappedInt](Seq(1, 2, 3, 4))
gives a message for each int like
type mismatch; found : Int(1) required: WrappedInt
import Conversions._
Also, the parameter onFoo
s constructor does not need to be implicit, but that does not matter. – Dima