Dániel Berecz
Dániel Berecz

Reputation: 1

Scala collection of classes with Implicit Ordering

I would like to create an Array (or List, ArrayBuffer, etc) which can only contain instance of classes with defined implicit Ordering (e.g. Int, Long, Double).

Something like this:

val ab = new ArrayBuffer[???]()
ab += 7
ab += 8.9
ab += 8L

I don't want to compare these values with each other.

Upvotes: 0

Views: 1062

Answers (2)

Jasper-M
Jasper-M

Reputation: 15086

If you really want to have a list of objects of different types and still be able to statically check things about the objects in that list at compile time, you would have to use something like a HList from shapeless. Here is an example of how you could have two heterogeneous lists and still check at compile time that each ith element of both lists can be compared with each other.

import shapeless._
import shapeless.ops.hlist.{LiftAll, Zip, Mapper}

object lt extends Poly1 { 
  implicit def instance[A] = at[(Ordering[A], A, A)] { 
    case (ord, a, b) => ord.lt(a, b)
  } 
}

def areLessThan[L <: HList, O <: HList, OLL <: HList](a: L, b: L)(
  implicit 
  ord: LiftAll.Aux[Ordering, L, O], 
  zip: Zip.Aux[O :: L :: L :: HNil, OLL], 
  map: Mapper[lt.type, OLL]
) = zip(ord.instances :: a :: b :: HNil).map(lt)

Using it:

scala> val a = 1 :: "b" :: Option(4L) :: HNil
a: Int :: String :: Option[Long] :: shapeless.HNil = 1 :: b :: Some(4) :: HNil

scala> val b = 2 :: "a" :: Option(7L) :: HNil
b: Int :: String :: Option[Long] :: shapeless.HNil = 2 :: a :: Some(7) :: HNil

scala> areLessThan(a, b)
res10: Boolean :: Boolean :: Boolean :: shapeless.HNil = true :: false :: true :: HNil

Upvotes: 0

Nagarjuna Pamu
Nagarjuna Pamu

Reputation: 14825

Just use the type class constraint as shown below

def createList[T: Ordering](values: T*) = values.toList

T: Ordering implies only type which has Ordering instance in scope are allowed to be passed as arguments to the function.

scala> def createList[T: Ordering](values: T*) = values.toList
createList: [T](values: T*)(implicit evidence$1: Ordering[T])List[T]

scala> case class Cat()
defined class Cat

scala> createList(1, 2, 3)
res2: List[Int] = List(1, 2, 3)

scala> createList(Cat())
<console>:15: error: No implicit Ordering defined for Cat.
       createList(Cat())
             ^

Integer ordering is available in scope but cat ordering is not available in scope. So you cannot pass Cat values until you provide instance of Ordering[Cat]

Now lets provide some fake ordering and see if compiler accepts Cat as argument

scala> implicit val orderingCat: Ordering[Cat] = (a: Cat, b: Cat) => ???
orderingCat: Ordering[Cat] = $anonfun$1@6be766d1

scala> createList(Cat())
res4: List[Cat] = List(Cat())

It works.

Upvotes: 1

Related Questions