Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic method convertible to structural type in scala

Tags:

generics

scala

Why can't I do this:

def compare[A <% { def toInt: Int }, 
            B <% { def toInt: Int }]
           (bs: Seq[A], is: Seq[B]): Boolean = { 
  bs.toArray.zip(is) forall { p => p._1.toInt == p._2.toInt } 
}

In order to compare any Sequence of types convertible to Int? How can I implement a similar pattern?


Update: this should run Message.compare(List(1.0, 2.0, 3.0), List(0, 0, 0))

like image 441
Hugo Sereno Ferreira Avatar asked Dec 07 '11 21:12

Hugo Sereno Ferreira


3 Answers

Since you're dealing with Arrays, you need ClassManifests.

def compare[A <% { def toInt: Int } : ClassManifest, 
            B <% { def toInt: Int } : ClassManifest]
            (bs: Seq[A], is: Seq[B]): Boolean = { 
  (bs.toArray, is).zipped.forall(_.toInt == _.toInt)
}

The compiler error message in this case couldn't be clearer IMO.

EDIT:

You don't really need to convert sequences to arrays. The following works just fine.

def compare[A <% { def toInt: Int }, 
            B <% { def toInt: Int }]
           (bs: Seq[A], is: Seq[B]): Boolean = { 
      (bs, is).zipped.forall(_.toInt == _.toInt)
}
like image 179
missingfaktor Avatar answered Nov 02 '22 04:11

missingfaktor


A good example of where the problem lies would seem to be doing this:

val a = implicitly[Int => { def toInt : Int }]
a(1).toInt

Scala 2.9.1 blows up at this point sometime in Runtime code - I can only think that this is a bug.

You can achieve the effect you're looking for using type classes, though: the following code works in all of your examples:

def compare[A : Numeric, B : Numeric](bs : Seq[A], cs : Seq[B]) = {
  (bs, cs).zipped.forall(implicitly[Numeric[A]].toInt(_) == implicitly[Numeric[B]].toInt(_))
}

This should also have the benefit of being faster than the version using structural types. If you need to add your own types in that should be convertible to integers, you can provide evidence for the Numeric typeclass in the same way as is done at https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/math/Numeric.scala#L1 for the standard values.

like image 32
Submonoid Avatar answered Nov 02 '22 06:11

Submonoid


It works fine for me when I remove the unnecessary toArray

def compare[A <% { def toInt: Int }, B <% { def toInt: Int }](bs: Seq[A], is: Seq[B]): Boolean = { bs.zip(is) forall { p => p._1.toInt == p._2.toInt } }
Class A{def toInt = 4}
Class B(i: Int) {def toInt = i}
compare (List(new A, new A), List(new B(3), new B(4))) //false
compare (List(new A, new A), List(new B(4), new B(4))) //true

Converting the Seq to an array would require you add an implicit ClassManifest, however Scala does not let you mix those with view bounds.

like image 23
Dan Simon Avatar answered Nov 02 '22 06:11

Dan Simon