Establishing a known 1:1 relationship between a type and a unique value

196 views Asked by At

Given that i have a kind of enumeration using a sealed trait and case objects representing the values, is it possible to enforce a mechanism to retrieve the single unique value for a given type, without requiring an implicit argument?

with implicits this would be

sealed trait Enum
sealed trait Value1 extends Enum
case object Value1 extends Value1 { implicit val me: Value1 = Value1 }
sealed trait Value2 extends Enum
case object Value2 extends Value2 { implicit val me: Value1 = Value1 }

def test[V <: Enum](implicit evidence: V): V = evidence

test[Value1]

is it possible to drop the implicit argument to test? that is, to ensure that V is a defined subtype of Enum (obviously, test[Enum] should fail). the gordian knot is:

object Enum {
  def unique[V <: Enum]: V = ???
}

?

1

There are 1 answers

3
Kevin Wright On BEST ANSWER

It looks as though you're trying to look up specific values or behaviour based on type and, for whatever reason don't want to put this behaviour in some common superclass.

It also looks as though you're trying to use implicits and companion objects to achieve this by emulating some of the characteristics of Java's statics. This is a model you absolutely want to be dropping from your mental toolkit, it's hopelessly broken from the perspective of object oriented programming.

What you really want is ad-hoc polymorphism, and we have better ways to do that. Interestingly, it does still use implicits, and a context bound:

sealed trait Enum
sealed trait Value1 extends Enum
sealed trait Value2 extends Enum

case object Value1 extends Value1
case object Value2 extends Value2

sealed abstract class UniqValue[T] { def value: T }
implicit object Value1HasUniq extends UniqValue[Value1] { val value = Value1 }
implicit object Value2HasUniq extends UniqValue[Value2] { val value = Value2 }

def test[V <: Enum : UniqValue]: V = implicitly[UniqValue[V]].value

You could also define test as:

def test[V <: Enum](implicit ev: UniqValue[V]): V = ev.value

Which does exactly the same thing, manually removing the syntactic sugar of context bounds that the compiler would do for you anyway.

Basically, it all works because the compile will also consider type params when resolving implicits.

If you want to read up more on this kind of thing, the magic prhrase to search for is "type classes"