Search code examples
scalacastingtype-conversionimplicit

Scala: Lambda cannot be cast to ClassTag error


I am trying to combine a view bound (motivation for using a deprecated bound will follow below) with an implicit ClassTag parameter. Here's in short the hierarchy of my classes (I omit code that seems irrelevant, you are welcomed to indicate if you need anything):

abstract class BaseSort [T <% Ordered[T]]{
//some methods
}

I made it an abstract class instead of a trait because traits do not support context and view bounds. I chose view over context because the former supports implicit conversion:

From 1st Ed of "Scala for the Impatient" (p. 234):

The <% relation means that T can be converted to a Comparable[T] through an implicit conversion

Then as an implementation of it I have class MergeSort:

//Segewick and Wayne's implementation re-written in Scala
class MergeSort [T <% Ordered[T]](implicit evidence: ClassTag[T]) extends BaseSort [T]{
  var aux: Array[T] = _

  def sort(a: Array[T]): Array[T] = {
    aux = new Array[T](a.length)
    sort(a, 0, a.length-1)
  }
...
}

The comment line before class signature is self-explanatory, but I think that this field var aux: Array[T] = _ is where my problem starts. If I follow the Algorithms' book then I shouldn't try to initialize this from the constructor.

Finally I have a variation of MergeSort implementation which has the following signature (it overrides sort method of MergeSort):

class Ex2_2_12[T<% Ordered[T]](implicit evidence: ClassTag[T]) extends MergeSort[T]{
...
}

Now when I try to run this test:

class Ex2_2_12Spec extends BaseSpec{

 ...

  "an array" should "at least be sorted" in {
    val test = "MERGEWITHSHELLSORTEXAMPLE".toCharArray
    val customMergeSort = new Ex2_2_12[Char]
    customMergeSort.sort(test)
    info(s"target array after sorting is ${test.toList}")
    assert(isSorted(test))
  }
}

this is what being thrown at me :-):

[info] an array
[info] - should at least be sorted *** FAILED ***
[info]   java.lang.ClassCastException: ca.vgorcinschi.algorithms_2.Ex2_2_12Spec$$Lambda$18483/698572900 cannot be cast to scala.reflect.ClassTag
[info]   at ca.vgorcinschi.algorithms_2.Ex2_2_12.<init>(Ex2_2_12.scala:21)
[info]   at ca.vgorcinschi.algorithms_2.Ex2_2_12Spec.$anonfun$new$2(Ex2_2_12Spec.scala:13)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   at org.scalatest.FlatSpecLike$$anon$1.apply(FlatSpecLike.scala:1682)
[info]   at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
[info]   at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
[info]   ...

The referenced in the stacktrace line 21 is the signature of Ex2_2_12. I tried two things:

  • Use a compound bound (class Ex2_2_12[T<% Ordered[T] with ClassTag[T]]) -same error
  • remove the ClassTag implicit parameter - then I get error for the referenced above aux field (Array[T]) that there is no ClassTag provided.

I will continue investigating, but if someone could give me a hint or direction that would be appreciated.


Solution

  • Well the solution in my case was (from the same book but 80 pages later) to get read of deprecated view bounds and to use compound context bounds:

    Base class:

    abstract class BaseSort [T : Ordering]{
    
      ...
    
      def less(v: T, w: T):Boolean = {
        import Ordered._
        v < w
      }
    ...
    }
    

    One level-up:

    class MergeSort [T : ClassTag : Ordering] extends BaseSort [T]{
      var aux: Array[T] = _
    ...
    }
    

    And almost identically the class that I need for my test:

    class Ex2_2_12[T : ClassTag : Ordering] extends MergeSort[T]{...}