In the following Scala code I have a sequence of currying functions with different signatures. I want to iterate through them and invoke.
def intCheck(b: Int)(a: Int) = a == b
def stringCheck(b: String)(a: String) = a == b
def doubleCheck(b: Double)(a: Double) = a == b
val list = Seq(intCheck(1) _, stringCheck("a") _, doubleCheck(2.3) _)
for (f <- list) {
//if f is 1st function
f(2) // LINE 1
//if f is 2nd function
f("a") // LINE 2
//if f is 3rd function
f(2.0) // LINE 3
}
But for the LINE 1,2 & 3 I get a compilation error "Type mismatch, expected: String with Int with Double, actual: Int". How can I enforce compiler to avoid type-check here if I am sure about the type here.
i think this is a way...
sealed abstract class AnyChecker(val a:Any,val b:Any) {
def eval = a==b
}
class IntChecker(override val a:Int,override val b:Int) extends AnyChecker(a,b)
class DoubleChecker(override val a:Double,override val b:Double) extends AnyChecker(a,b)
class StringChecker(override val a:String,override val b:String) extends AnyChecker(a,b)
object IntChecker {
def apply(a:Int,b:Int) = new IntChecker(a,b)
def unapply(intChecker: IntChecker) = Some(intChecker.a,intChecker.b)
}
object DoubleChecker {
def apply(a:Double,b:Double) = new DoubleChecker(a,b)
def unapply(doubleChecker: DoubleChecker) = Some(doubleChecker.a,doubleChecker.b)
}
object StringChecker {
def apply(a:String,b:String) = new StringChecker(a,b)
def unapply(stringChecker: StringChecker) = Some(stringChecker.a,stringChecker.b)
}
val list = List(IntChecker(1,3), StringChecker("a","a"), DoubleChecker(2.3,3.1), StringChecker("a","b"), StringChecker("a","c"), StringChecker("x","x"))
for (f <- list) {
f match {
case a:IntChecker => println(s"a:${a.a}, b:${a.b}, ${a.eval}")
case a:DoubleChecker => println(s"a:${a.a}, b:${a.b}, ${a.eval}")
case StringChecker("a","a") => println("equals")
case StringChecker("a","b") => println("not equals")
case StringChecker("a",b) => println( StringChecker("a",b).eval)
case StringChecker(a,b) => println(StringChecker(a,b).eval)
}
}
Please, check this, i am sure this will help you.
http://bplawler.tumblr.com/post/7493366722/scala-programming-unapply-and-case-classes
Also, another simple way is only using AnyChcker....
class AnyChecker[T](val a: T, val b: T) {
def eval = a == b
override def toString = s"Checker($a,$b)"
}
object AnyChecker {
def apply[T](a: T, b: T) = new AnyChecker(a, b)
def unapply[T](doubleChecker: AnyChecker[T]) = Some(doubleChecker.a, doubleChecker.b)
}
val list2 = List(AnyChecker(1, 3), AnyChecker("a", "a"), AnyChecker(2.3, 3.1), AnyChecker("a", "b"), AnyChecker("a", "c"), AnyChecker("x", "x"))
for (checker <- list2) {
checker match {
case AnyChecker(1, 3) => println("ints")
case AnyChecker(2.3, 3.1) => println("doubles")
case AnyChecker("a","a") => println("double a")
case checker1: AnyChecker[Any] =>println(checker1)
}
}
if you only write like a tupes....
val list3 = List((1, "1"), ("a", "a"), (2.3, 3.1), ("a", "b"), ("a", "c"), ("x", "x")).map(element=>AnyChecker(element._1,element._2))
for (checker <- list3) {
checker match {
case AnyChecker(1, 3) => println("ints")
case AnyChecker(2.3, 3.1) => println("doubles")
case AnyChecker("a","a") => println("double a")
case checker1: AnyChecker[Any] =>println(checker1.eval)
}
}
Related
I have an iterable of arrays that I am trying to turn into case classes, and I'm mapping over them to do so. In the event of an array being non-convertable to a case class, I want to log a warning and proceed with the mapping. However, when I implement the warning, the return type changes from Iterable[MyCaseClass] to Iterable[Any] which is not what I want. E.g.:
case class MyCaseClass(s1: String, s2: String)
object MyCaseClass {
def apply(sa: Array[String]) = new MyCaseClass(sa(0), sa(1))
}
val arrayIterable: Iterable[Array[String]] = Iterable(Array("a", "b"), Array("a", "b", "c"))
def badReturnType(): Iterable[Any] = { // Iterable[Any] is undesireable
arrayIterable map {
case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
case _ => println("something bad happened!") // but warnings are good
}
}
def desiredReturnType(): Iterable[MyCaseClass] = { // Iterable[MyCaseClass] is desireable
arrayIterable map {
case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
// but no warnings if things go wrong!
}
}
I want to write a function that meets the following criteria:
maps over the Iterable, converting each element to a MyCaseClass
log warnings when I get an array that cant be converted to a MyCaseClass
after logging the warning, the array passed into the match criteria is ignored/discarded
the return type should be Iterable[MyCaseClass].
How can I meet these conditions?
Consider using List instead of Array, and try wrapping in Option in combination with flatMap
l flatMap {
case e if e.length == 2 => Some(MyCaseClass(e))
case e => println(s"$e is wrong length"); None
}
Another approach is partitionMap
val (lefts, rights) = l.partitionMap {
case e if e.size == 2 => Right(MyCaseClass(e))
case e => Left(s"$e is wrong length")
}
lefts.foreach(println)
rights
You can do something like this:
final case class MyCaseClass(s1: String, s2: String)
def parse(input: Array[String]): Either[String, MyCaseClass] = input match {
case Array(s1, s2) => Right(MyCaseClass(s1, s2))
case _ => Left(s"Bad input: ${input.mkString("[", ", ", "]")}")
}
def logErrors(validated: Either[String, _]): Unit = validated match {
case Left(error) => println(error)
case Right(_) => ()
}
def validateData(data: IterableOnce[Array[String]]): List[MyCaseClass] =
data
.iterator
.map(parse)
.tapEach(logErrors)
.collect {
case Right(value) => value
}.toList
Which you can use like this:
val arrayIterable = Iterable(Array("a", "b"), Array("a", "b", "c"))
validateData(arrayIterable)
// Bad input: [a, b, c]
// res14: List[MyCaseClass] = List(MyCaseClass("a", "b"))
I am currently working on a function that takes in a Map[String, List[String]] and a String as arguments. The map contains a user Id and the IDs of films that they liked. What I need to do is, to return a List[List[String]] which contains the other movies that where liked by the user who liked the movie that was passed into the function.
The function declaration looks as follows:
def movies(m: Map[String, List[String]], mov: String) : List[List[String]]= {
}
So lets imagine the following:
val m1 : [Map[Int, List[String]]] = Map(1 ‐> List("b", "a"), 2 ‐> List("y", "x"), 3 ‐> List("c", "a"))
val movieID = "a"
movies(m1, movieId)
This should return:
List(List("b"), List("c"))
I have tried using
m1.filter(x => x._2.contains(movieID))
So that only Lists containing movieID are kept in the map, but my problem is that I need to remove movieID from every list it occurs in, and then return the result as a List[List[String]].
You could use collect:
val m = Map("1" -> List("b", "a"), "2" -> List("y", "x"), "3" -> List("c", "a"))
def movies(m: Map[String, List[String]], mov: String) = m.collect {
case (_, l) if l.contains(mov) => l.filterNot(_ == mov)
}
movies(m, "a") //List(List(b), List(c))
Problem with this approach is, that it would iterate over every movie list twice, the first time with contains and the second time with filterNot. We could optimize it tail-recursive function, which would look for element and if found just return list without it:
import scala.annotation.tailrec
def movies(m: Map[String, List[String]], mov: String) = {
#tailrec
def withoutElement[T](l: List[T], mov: T, acc: List[T] = Nil): Option[List[T]] = {
l match {
case x :: xs if x == mov => Some(acc.reverse ++ xs)
case x :: xs => withoutElement(xs, mov, x :: acc)
case Nil => None
}
}
m.values.flatMap(withoutElement(_, mov))
}
The solution from Krzysztof is a good one. Here's an alternate way to traverse every List just once.
def movies(m: Map[String, List[String]], mov: String) =
m.values.toList.flatMap{ss =>
val tpl = ss.foldLeft((false, List.empty[String])){
case ((_,res), `mov`) => (true, res)
case ((keep,res), str) => (keep, str::res)
}
if (tpl._1) Some(tpl._2) else None
}
This should work for you:
object DemoAbc extends App {
val m1 = Map(1 -> List("b", "a"), 2 -> List("y", "x"), 3 -> List("c", "a"))
val movieID = "a"
def movies(m: Map[Int, List[String]], mov: String): List[List[String]] = {
val ans = m.foldLeft(List.empty[List[String]])((a: List[List[String]], b: (Int, List[String])) => {
if (b._2.contains(mov))
b._2.filter(_ != mov) :: a
else a
})
ans
}
print(movies(m1, movieID))
}
Below code uses case statement to determine which distance function should be applied manhattanDistance or eucleudianDistance
Can this code be generalized further using traits or DRY principle so it is more maintainable ?
object general {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
object DistanceOptions extends Enumeration {
type Dist = Value
val Manhattan, Eucleudian = Value
}
object DistanceFunctions {
def manhattanDistance(l1: (DataLine, DataLine)): Double = {
val t: List[(Double, Double)] = l1._1.points.zip(l1._2.points)
t.map(m => Math.abs(m._1 - m._2)).sum
}
def eucleudianDistance(l1: (DataLine, DataLine)): Double = {
val ld: List[(Double, Double)] = l1._1.points.zip(l1._2.points)
val sum = ld.map(m => Math.abs(m._1 - m._2) + Math.abs(m._1 - m._2)).sum
Math.sqrt(sum)
}
def getDistance(s: DistanceOptions.Dist, l1: (DataLine, DataLine)) = {
s match {
case DistanceOptions.Manhattan => DistanceFunctions.manhattanDistance(l1)
case DistanceOptions.Eucleudian => DistanceFunctions.eucleudianDistance(l1)
}
DistanceFunctions.manhattanDistance(l1)
DistanceFunctions.eucleudianDistance(l1)
}
}
case class DataLine(label: String, points: List[Double])
val l = (DataLine("a", List(1, 2)), DataLine("b", List(1, 2)))
//> l : (general.DataLine, general.DataLine) = (DataLine(a,List(1.0, 2.0)),Dat
//| aLine(b,List(1.0, 2.0)))
DistanceFunctions.getDistance(DistanceOptions.Manhattan, l)
//> res0: Double = 0.0
DistanceFunctions.getDistance(DistanceOptions.Eucleudian, l)
//> res1: Double = 0.0
}
Updated using type classes :
object gen extends App {
object DistanceOptions extends Enumeration {
type Dist = Value
val Manhattan, Eucleudian = Value
}
trait DistanceFunctionsType[T, A] {
def manhattanDistance(t: (T, T)): A
def eucleudianDistance(t: (T, T)): A
}
object DistanceFunctions extends DistanceFunctionsType[DataLine, Double] {
def manhattanDistance(l1: (DataLine, DataLine)): Double = {
val t: List[(Double, Double)] = l1._1.points.zip(l1._2.points)
t.map(m => Math.abs(m._1 - m._2)).sum
}
def eucleudianDistance(l1: (DataLine, DataLine)): Double = {
val ld: List[(Double, Double)] = l1._1.points.zip(l1._2.points)
val sum = ld.map(m => Math.abs(m._1 - m._2) + Math.abs(m._1 - m._2)).sum
Math.sqrt(sum)
}
def getDistance(distanceOptions: DistanceOptions.Dist, l1: (DataLine, DataLine)) = {
distanceOptions match {
case DistanceOptions.Manhattan => DistanceFunctions.manhattanDistance(l1)
case DistanceOptions.Eucleudian => DistanceFunctions.eucleudianDistance(l1)
}
}
}
case class DataLine(label: String, points: List[Double])
val l = (DataLine("a", List(1, 2)), DataLine("b", List(1, 2)))
println(DistanceFunctions.getDistance(DistanceOptions.Manhattan, l))
println(DistanceFunctions.getDistance(DistanceOptions.Eucleudian, l))
}
In implementing this structure I found this guide helpful : http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
Yes—see for example Spire's MetricSpace, which would allow you to write something like this:
case class DataLine(points: List[Double])
import spire.algebra._
object manhattanDistance extends MetricSpace[DataLine, Double] {
def distance(v: DataLine, w: DataLine): Double = {
val ld: List[(Double, Double)] = v.points.zip(w.points)
val sum = ld.map(m =>
math.abs(m._1 - m._2) + math.abs(m._1 - m._2)
).sum
math.sqrt(sum)
}
}
This approach allows you to avoid the enumeration, and if you use Spire's implementation you get nice operators, a clean way to test that your implementation satisfies e.g. the triangle inequality, and the benefit of a lot of smart people having thought about performance, specialization, etc. for you.
I want to write a scala macros that can override field values of case class based on map entries with simple type check.
In case original field type and override value type are compatible set new value otherwise keep original value.
So far I have following code:
import language.experimental.macros
import scala.reflect.macros.Context
object ProductUtils {
def withOverrides[T](entity: T, overrides: Map[String, Any]): T =
macro withOverridesImpl[T]
def withOverridesImpl[T: c.WeakTypeTag](c: Context)
(entity: c.Expr[T], overrides: c.Expr[Map[String, Any]]): c.Expr[T] = {
import c.universe._
val originalEntityTree = reify(entity.splice).tree
val originalEntityCopy = entity.actualType.member(newTermName("copy"))
val originalEntity =
weakTypeOf[T].declarations.collect {
case m: MethodSymbol if m.isCaseAccessor =>
(m.name, c.Expr[T](Select(originalEntityTree, m.name)), m.returnType)
}
val values =
originalEntity.map {
case (name, value, ctype) =>
AssignOrNamedArg(
Ident(name),
{
def reifyWithType[K: WeakTypeTag] = reify {
overrides
.splice
.asInstanceOf[Map[String, Any]]
.get(c.literal(name.decoded).splice) match {
case Some(newValue : K) => newValue
case _ => value.splice
}
}
reifyWithType(c.WeakTypeTag(ctype)).tree
}
)
}.toList
originalEntityCopy match {
case s: MethodSymbol =>
c.Expr[T](
Apply(Select(originalEntityTree, originalEntityCopy), values))
case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
}
}
}
Executed like this:
import macros.ProductUtils
case class Example(field1: String, field2: Int, filed3: String)
object MacrosTest {
def main(args: Array[String]) {
val overrides = Map("field1" -> "new value", "field2" -> "wrong type")
println(ProductUtils.withOverrides(Example("", 0, ""), overrides)) // Example("new value", 0, "")
}
}
As you can see, I've managed to get type of original field and now want to pattern match on it in reifyWithType.
Unfortunately in current implementation I`m getting a warning during compilation:
warning: abstract type pattern K is unchecked since it is eliminated by erasure case Some(newValue : K) => newValue
and a compiler crash in IntelliJ:
Exception in thread "main" java.lang.NullPointerException
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.preEraseAsInstanceOf$1(Erasure.scala:1032)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.preEraseNormalApply(Erasure.scala:1083)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.preEraseApply(Erasure.scala:1187)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.preErase(Erasure.scala:1193)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.transform(Erasure.scala:1268)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.transform(Erasure.scala:1018)
at scala.reflect.internal.Trees$class.itransform(Trees.scala:1217)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:13)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:13)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2897)
at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:48)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.transform(Erasure.scala:1280)
at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anon$1.transform(Erasure.scala:1018)
So the questions are:
* Is it possible to make type comparison of type received in macro to value runtime type?
* Or is there any better approach to solve this task?
After all I ended up with following solution:
import language.experimental.macros
import scala.reflect.macros.Context
object ProductUtils {
def withOverrides[T](entity: T, overrides: Map[String, Any]): T =
macro withOverridesImpl[T]
def withOverridesImpl[T: c.WeakTypeTag](c: Context)(entity: c.Expr[T], overrides: c.Expr[Map[String, Any]]): c.Expr[T] = {
import c.universe._
val originalEntityTree = reify(entity.splice).tree
val originalEntityCopy = entity.actualType.member(newTermName("copy"))
val originalEntity =
weakTypeOf[T].declarations.collect {
case m: MethodSymbol if m.isCaseAccessor =>
(m.name, c.Expr[T](Select(c.resetAllAttrs(originalEntityTree), m.name)), m.returnType)
}
val values =
originalEntity.map {
case (name, value, ctype) =>
AssignOrNamedArg(
Ident(name),
{
val ruClass = c.reifyRuntimeClass(ctype)
val mtag = c.reifyType(treeBuild.mkRuntimeUniverseRef, Select(treeBuild.mkRuntimeUniverseRef, newTermName("rootMirror")), ctype)
val mtree = Select(mtag, newTermName("tpe"))
def reifyWithType[K: c.WeakTypeTag] = reify {
def tryNewValue[A: scala.reflect.runtime.universe.TypeTag](candidate: Option[A]): Option[K] =
if (candidate.isEmpty) {
None
} else {
val cc = c.Expr[Class[_]](ruClass).splice
val candidateValue = candidate.get
val candidateType = scala.reflect.runtime.universe.typeOf[A]
val expectedType = c.Expr[scala.reflect.runtime.universe.Type](mtree).splice
val ok = (cc.isPrimitive, candidateValue) match {
case (true, _: java.lang.Integer) => cc == java.lang.Integer.TYPE
case (true, _: java.lang.Long) => cc == java.lang.Long.TYPE
case (true, _: java.lang.Double) => cc == java.lang.Double.TYPE
case (true, _: java.lang.Character) => cc == java.lang.Character.TYPE
case (true, _: java.lang.Float) => cc == java.lang.Float.TYPE
case (true, _: java.lang.Byte) => cc == java.lang.Byte.TYPE
case (true, _: java.lang.Short) => cc == java.lang.Short.TYPE
case (true, _: java.lang.Boolean) => cc == java.lang.Boolean.TYPE
case (true, _: Unit) => cc == java.lang.Void.TYPE
case _ =>
val args = candidateType.asInstanceOf[scala.reflect.runtime.universe.TypeRefApi].args
if (!args.contains(scala.reflect.runtime.universe.typeOf[Any])
&& !(candidateType =:= scala.reflect.runtime.universe.typeOf[Any]))
candidateType =:= expectedType
else cc.isInstance(candidateValue)
}
if (ok)
Some(candidateValue.asInstanceOf[K])
else None
}
tryNewValue(overrides.splice.get(c.literal(name.decoded).splice)).getOrElse(value.splice)
}
reifyWithType(c.WeakTypeTag(ctype)).tree
}
)
}.toList
originalEntityCopy match {
case s: MethodSymbol =>
c.Expr[T](
Apply(Select(originalEntityTree, originalEntityCopy), values))
case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
}
}
}
It kind of satisfies original requirements:
class ProductUtilsTest extends FunSuite {
case class A(a: String, b: String)
case class B(a: String, b: Int)
case class C(a: List[Int], b: List[String])
case class D(a: Map[Int, String], b: Double)
case class E(a: A, b: B)
test("simple overrides works"){
val overrides = Map("a" -> "A", "b" -> "B")
assert(ProductUtils.withOverrides(A("", ""), overrides) === A("A", "B"))
}
test("simple overrides works 1"){
val overrides = Map("a" -> "A", "b" -> 1)
assert(ProductUtils.withOverrides(B("", 0), overrides) === B("A", 1))
}
test("do not override if types do not match"){
val overrides = Map("a" -> 0, "b" -> List("B"))
assert(ProductUtils.withOverrides(B("", 0), overrides) === B("", 0))
}
test("complex types also works"){
val overrides = Map("a" -> List(1), "b" -> List("A"))
assert(ProductUtils.withOverrides(C(List(0), List("")), overrides) === C(List(1), List("A")))
}
test("complex types also works 1"){
val overrides = Map("a" -> List(new Date()), "b" -> 2.0d)
assert(ProductUtils.withOverrides(D(Map(), 1.0), overrides) === D(Map(), 2.0))
}
test("complex types also works 2"){
val overrides = Map("a" -> A("AA", "BB"), "b" -> 2.0d)
assert(ProductUtils.withOverrides(E(A("", ""), B("", 0)), overrides) === E(A("AA", "BB"), B("", 0)))
}
}
Unfortunatelly because of type erasure in Java/Scala it is hard to force type equality before changing value to new value, so you can do something like this:
scala> case class C(a: List[Int], b: List[String])
defined class C
scala> val overrides = Map("a" -> List(new Date()), "b" -> List(1.0))
overrides: scala.collection.immutable.Map[String,List[Any]] = Map(a -> List(Mon Aug 26 15:52:27 CEST 2013), b -> List(1.0))
scala> ProductUtils.withOverrides(C(List(0), List("")), overrides)
res0: C = C(List(Mon Aug 26 15:52:27 CEST 2013),List(1.0))
scala> res0.a.head + 1
java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:106)
at .<init>(<console>:14)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
Is there a reason that match written against Seq would work differently on IndexedSeq types than the way it does on LinearSeq types? To me it seems like the code below should do the exact same thing regardless of the input types. Of course it doesn't or I wouldn't be asking.
import collection.immutable.LinearSeq
object vectorMatch {
def main(args: Array[String]) {
doIt(Seq(1,2,3,4,7), Seq(1,4,6,9))
doIt(List(1,2,3,4,7), List(1,4,6,9))
doIt(LinearSeq(1,2,3,4,7), LinearSeq(1,4,6,9))
doIt(IndexedSeq(1,2,3,4,7), IndexedSeq(1,4,6,9))
doIt(Vector(1,2,3,4,7), Vector(1,4,6,9))
}
def doIt(a: Seq[Long], b: Seq[Long]) {
try {
println("OK! " + m(a, b))
}
catch {
case ex: Exception => println("m(%s, %s) failed with %s".format(a, b, ex))
}
}
#annotation.tailrec
def m(a: Seq[Long], b: Seq[Long]): Seq[Long] = {
a match {
case Nil => b
case firstA :: moreA => b match {
case Nil => a
case firstB :: moreB if (firstB < firstA) => m(moreA, b)
case firstB :: moreB if (firstB > firstA) => m(a, moreB)
case firstB :: moreB if (firstB == firstA) => m(moreA, moreB)
case _ => throw new Exception("Got here: a: " + a + " b: " + b)
}
}
}
}
Running this on 2.9.1 final, I get the following output:
OK! List(2, 3, 4, 7)
OK! List(2, 3, 4, 7)
OK! List(2, 3, 4, 7)
m(Vector(1, 2, 3, 4, 7), Vector(1, 4, 6, 9)) failed with scala.MatchError: Vector(1, 2, 3, 4, 7) (of class scala.collection.immutable.Vector)
m(Vector(1, 2, 3, 4, 7), Vector(1, 4, 6, 9)) failed with scala.MatchError: Vector(1, 2, 3, 4, 7) (of class scala.collection.immutable.Vector)
It runs fine for List-y things, but fails for Vector-y things. Am I missing something? Is this a compiler bug?
The scalac -print output for m looks like:
#scala.annotation.tailrec def m(a: Seq, b: Seq): Seq = {
<synthetic> val _$this: object vectorMatch = vectorMatch.this;
_m(_$this,a,b){
<synthetic> val temp6: Seq = a;
if (immutable.this.Nil.==(temp6))
{
b
}
else
if (temp6.$isInstanceOf[scala.collection.immutable.::]())
{
<synthetic> val temp8: scala.collection.immutable.:: = temp6.$asInstanceOf[scala.collection.immutable.::]();
<synthetic> val temp9: Long = scala.Long.unbox(temp8.hd$1());
<synthetic> val temp10: List = temp8.tl$1();
val firstA$1: Long = temp9;
val moreA: List = temp10;
{
<synthetic> val temp1: Seq = b;
if (immutable.this.Nil.==(temp1))
{
a
}
else
if (temp1.$isInstanceOf[scala.collection.immutable.::]())
{
<synthetic> val temp3: scala.collection.immutable.:: = temp1.$asInstanceOf[scala.collection.immutable.::]();
<synthetic> val temp4: Long = scala.Long.unbox(temp3.hd$1());
<synthetic> val temp5: List = temp3.tl$1();
val firstB: Long = temp4;
if (vectorMatch.this.gd1$1(firstB, firstA$1))
body%11(firstB){
_m(vectorMatch.this, moreA, b)
}
else
{
val firstB: Long = temp4;
val moreB: List = temp5;
if (vectorMatch.this.gd2$1(firstB, moreB, firstA$1))
body%21(firstB,moreB){
_m(vectorMatch.this, a, moreB)
}
else
{
val firstB: Long = temp4;
val moreB: List = temp5;
if (vectorMatch.this.gd3$1(firstB, moreB, firstA$1))
body%31(firstB,moreB){
_m(vectorMatch.this, moreA, moreB)
}
else
{
body%41(){
throw new java.lang.Exception("Got here: a: ".+(a).+(" b: ").+(b))
}
}
}
}
}
else
{
body%41()
}
}
}
else
throw new MatchError(temp6)
}
};
You can't use :: for anything other than List. The Vector is failing to match because :: is a case class that extends List, so its unapply method does not work for Vector.
val a :: b = List(1,2,3) // fine
val a :: b = Vector(1,2,3) // error
But you can define your own extractor that works for all sequences:
object +: {
def unapply[T](s: Seq[T]) =
s.headOption.map(head => (head, s.tail))
}
So you can do:
val a +: b = List(1,2,3) // fine
val a +: b = Vector(1,2,3) // fine
Followed pattern match works for List, Seq, LinearSeq, IndexedSeq, Vector.
Vector(1,2) match {
case a +: as => s"$a + $as"
case _ => "empty"
}
In Scala 2.10 object +: was introduced at this commit. Since then, for every SeqLike, you can do:
#annotation.tailrec
def m(a: Seq[Long], b: Seq[Long]): Seq[Long] = {
a match {
case Nil => b
case firstA +: moreA => b match {
case Nil => a
case firstB +: moreB if (firstB < firstA) => m(moreA, b)
case firstB +: moreB if (firstB > firstA) => m(a, moreB)
case firstB +: moreB if (firstB == firstA) => m(moreA, moreB)
case _ => throw new Exception("Got here: a: " + a + " b: " + b)
}
}
}
Code run at Scastie.