Skip to content

Instantly share code, notes, and snippets.

@colindean
Created September 15, 2014 19:20
Show Gist options
  • Save colindean/ef062db69915b49d44b9 to your computer and use it in GitHub Desktop.
Save colindean/ef062db69915b49d44b9 to your computer and use it in GitHub Desktop.
Trying to improve compareTo to be more idiomatic in Scala pattern matching
// This sucks
3.compareTo(4) match {
case x if x > 0 =>
case x if x < 0 =>
case x if x == 0 =>
}
/*
I want something like this:
3.compareTo(4) match {
case GreaterThan =>
case LessThan =>
case EqualTo =>
}
This would also be acceptable:
3.compareTo(4) match {
case GreaterThan(_) =>
case LessThan(_) =>
case EqualTo(_) =>
}
*/
// But this doesn't seem to work as I'd expect
case class GreaterThan(implicit x: Int) {
def apply(implicit x: Int):Boolean = x > 0
}
case class LessThan(implicit x: Int) {
def apply(implicit x: Int) = x < 0
}
case class EqualTo(implicit x: Int) {
def apply(implicit x: Int) = x == 0
}
3.compareTo(4) match {
case GreaterThan =>
case LessThan =>
case EqualTo =>
}
// I also tried with object apply(). This is something close, but not quite what I want.
object Compare {
def apply(x: Int) = {
x match {
case _ if x > 0 => GreaterThan
case _ if x < 0 => LessThan
case _ if x == 0 => EqualTo
}
}
case class GreaterThan()
case class LessThan()
case class EqualTo()
Compare(3.compareTo(4)) match {
case GreaterThan =>
case LessThan =>
case EqualTo =>
}
// I tried to implement something like this:
Compare(3).to(4) match {
case GreaterThan =>
case LessThan =>
case EqualTo =>
}
// but got caught up on the types:
case class GreaterThan()
case class LessThan()
case class EqualTo()
object Compare {
def apply(x: Ordered) = new Compare(x)
class Compare(original: Ordered) {
def to[T](y: T <: Ordered){ // can't seem to get this right
original.compareTo(y) match { // so that compareTo() gets the correctly typed `y`
case _ if x > 0 => GreaterThan
case _ if x < 0 => LessThan
case _ if x == 0 => EqualTo
}
}}
}
}
@Sciss
Copy link

Sciss commented Sep 15, 2014

Something like this. I would use [T: Ordering] instead of [T <% Ordered[T]], though.

sealed trait Comparison
case object GreaterThan extends Comparison
case object LessThan    extends Comparison
case object EqualTo     extends Comparison

object Compare {
  def apply[T <% Ordered[T]](x: T) = new Compare[T](x)
}

final class Compare[T](private val original: Ordered[T]) extends AnyVal {
  def to(y: T) = original.compareTo(y) match {
    case x if x >  0 => GreaterThan
    case x if x <  0 => LessThan
    case x if x == 0 => EqualTo
  }
}

Compare(4) to 5

@colindean
Copy link
Author

👍 I like that a lot.

@jsuereth
Copy link

@colindean - The failure here is not immediately trying to make "compareTo" a macro (especially after the talk), like:

compare(x,y, {
   case GreaterThan => ...
   case LessThan => ...
   case EqualTo =>...
})

AND THEN going for gold by using symbol instead of named objects, like:

compare(x,y, {
   case > =>
   case < =>
   case _ =>
  })

FYI -

> toolBox.parse("""{
     |    case < => 1
     |    case == => 2
     |    case > => 3
     | }""")
res8: res6.u.Tree = 
<empty> match {
  case $less => 1
  case $eq$eq => 2
  case $greater => 3
}

In a more serious note, @Sciss suggestion is great.

@faucct
Copy link

faucct commented Jul 10, 2016

Should not something like this work?

object Greater {
  def unapply(x: Int) = x > 0
}
object Equal {
  def unapply(x: Int) = x == 0
}
object Lesser {
  def unapply(x: Int) = x < 0
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment