You could specify result type:
val rescala> val result: List[Int] = "string" :: l
<console>:8: error: type mismatch;
found : String
required: Int
val result: List[Int] = "string" :: l
^
You could also create your own invariant methods like this:
def prepend[T1, T2](t: T1, l: List[T2])(implicit e: T1 =:= T2) = e(t) :: l
prepend(0, l)
// List[Int] = List(0, 1, 2, 3)
scala> prepend("str", l)
<console>:10: error: Cannot prove that String =:= Int.
prepend("str", l)
^
With value classes you could create invariant wrapper for List
without runtime penalty like this:
case class InvariantList[T](l: List[T]) extends AnyVal {
def ::(t: T) = InvariantList(t :: l)
}
val l = InvariantList(1 :: 2 :: 3 :: Nil)
0 :: l
// InvariantList(List(0, 1, 2, 3))
scala> "str" :: l
<console>:13: error: type mismatch;
found : String
required: Int
"str" :: l
^
You could also use invariant methods from scalaz
for collections concatenation:
import scalaz._, Scalaz._
List(0) |+| List(1, 2, 3)
// List(0, 1, 2, 3)
Vector('a) |+| Vector('b, 'c)
// Vector('a, 'b, 'c)
scala> List("string") |+| List(1, 2, 3)
<console>:14: error: type mismatch;
found : Int(1)
required: String
List("string") |+| List(1, 2, 3)
^
Note that (as mentioned by @drexin) there is an invariant list in scalaz: IList.