As pointed out by Alexey, you should specify the return type of Result's map (otherwise the Failure type is ambiguous, although you can specify the Failure type instead Failure[B]):
sealed trait Result[A]{
// specify the return type Result[B]
def map[B](f: A => B): Result[B] = this match{
case Success((a, rest)) => Success((f(a), rest))
case Failure(m) => Failure(m) // alternatively use Failure[B](m) here
}
}
case class Success[A](result: (A, List[Char])) extends Result[A]
case class Failure[A](message: String) extends Result[A]
object Utils{
def map[A,B](r: Result[A], f: A => B):Result[B] = {
r.map(f)
}
}
This will give a Failure the correct type:
scala> val f = Failure[Int]("oops")
Failure("oops"): Failure[Int]
scala> Utils.map(f, {i: Int => 4})
Failure("oops"): Result[Int]
vs (with the covariant solution below):
scala> Utils.map(f, {i: Int => 4})
Failure(oops): Product with Serializable with Result[_37] forSome { type _37 <: Int }
which is not what you want!
Originally I suggested that you make your types covariant (the +A):
sealed trait Result[+A]{
def map[B](f: A => B) = this match{
case Success((a, rest)) => Success((f(a), rest))
case Failure(m) => Failure(m)
}
}
case class Success[A](result: (A, List[Char])) extends Result[A]
case class Failure[A](message: String) extends Result[A]
object Utils{
def map[A,B](r: Result[A], f: A => B):Result[B] = {
r.map(f)
}
}
See this great blogpost for more discussion.
To quote part of the post:
Subtype Relationships
Assume that class Orange extends Fruit holds.
If class Box[A] is declared, then A can be prefixed with + or -.
A without annotation is invariant, i.e.:
Box[Orange] has no inheritance relationship to Box[Fruit].
+A is covariant, i.e.:
Box[Orange] is a subtype of Box[Fruit].
var f: Box[Fruit] = new Box[Orange]() is allowed.
-A is contravariant, i.e.:
Box[Fruit] is a subtype of Box[Orange].
var f: Box[Orange] = new Box[Fruit]() is allowed.