Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define anything that extends this trait

Refer to the following code snippet:

 trait Fruit {
   val color:String
   def == (fruit:Fruit) = this.color == fruit.color
 }

 case class Orange(color:String) extends Fruit 

 case class Apple(color:String) extends Fruit

As expected, Orange("red") == Orange("red") is true. However, I would like to enforce that only the same type of fruits can be compared, so for instance Orange("red") == Apple("red") should give an error. Can we enforce this in the signature of == in trait Fruit in an elegant way?

EDIT: I want the error to be caught at compile time, not at runtime.

like image 346
Jus12 Avatar asked Dec 06 '22 22:12

Jus12


2 Answers

Scalaz has an Equal "type class" that solves this problem, albeit with a different operator.

https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Equal.scala

The heart of it is basically this (although I use === where they use some unicode)

/** Defines a type safe === operator */
trait Equals[A] {
 def ===(y : A) : Boolean
}

/** A conventient way to define Equals traits based on the == operator */
def equalA[A](x : A) = new Equals[A] {
  def ===(y : A) = x == y
}

And is used like so

// one for oranges
implicit val EqualsOrange = equalA[Orange] _

// one for apples
implicit val EqualsApple = equalA[Apple] _


Orange("red") === Orange("red") // true

Orange("red") === Orange("green") // false

Orange("red") === Apple("red") // Compile error
like image 142
James Iry Avatar answered Dec 25 '22 21:12

James Iry


Unfortunately, you can't check this statically... At least, not using ==, which uses Java's Object#equals method where everything is emphatically defined in terms of raw objects.

If you want type safety, then your only choice is to implement another operator, perhaps something like =|=, then combine this with type classes to ensure your safety.

I believe that scalaz also has something useful for type-safe equality, but don't know the library well enough to state this for certain.

The other approach you can take, which will only be safe at runtime, is to use the canEqual pattern as described here. This is already used by case classes and offers a nice way to selectively break the LSP when it's appropriate for equality.

like image 37
Kevin Wright Avatar answered Dec 25 '22 20:12

Kevin Wright