Pregunta

I am trying to use the typeclass pattern in Scala to mark all the valid API serializable types, so that we can have compile-time safety around what we serialize. Our underlying library accepts an AnyRef which can lead to weird errors when not explicitly declaring the type before serializing it.

We allow sending out a public model, an iterable of public models, an option of public model, or a unit.

trait PublicModel
case class UserModel(name: String) extends PublicModel
sealed class SafeForPublic[-T]
implicit object PublicModelOk extends SafeForPublic[PublicModel]
implicit object IterablePublicModelOk extends SafeForPublic[Iterable[PublicModel]]
implicit object OptionPublicModelOk extends SafeForPublic[Option[PublicModel]]
implicit object UnitOk extends SafeForPublic[Unit]

This method works well for everything except methods where the parameter type is an option. This is because None is an Option[Nothing], so T = Nothing which will tell the compiler to look up an implicit object of type SafeForPublic[Nothing] and it will find both SafeForPublic[PublicModel] as well as SafeForPublic[Iterable[PublicModel]]

def boxed[T : SafeForPublic](t: Option[T]) = println("wooohoo!")

boxed(Some(None))  // works
boxed(Some(1))  // doesn't compile. Int is not a valid serializable model.
boxed(Some({}))  // works
boxed(Some(UserModel("ok")))  // works
boxed(Some(Seq(UserModel("ok"))))  // works
boxed(None) // doesn't compile, duplicate implicits ><

Any idea how I can trick the compiler to not find duplicate implicits for Nothing. I saw Miles Sabin had a trick using:

sealed trait NotNothing[A]{
  type B
}
object NotNothing {
  implicit val nothing = new NotNothing[Nothing]{ type B = Any }
  implicit def notNothing[A] = new NotNothing[A]{ type B = A }
}

But I couldn't figure out how to use it. Halp?

¿Fue útil?

Solución

Ok, thanks to some help from the Scala IRC channel, I figured out that LowPriority implicits was created to solve this issue.

I used this to fix it:

sealed class SafeForPublic[-T]
trait LowPriorityImplicits {
  implicit object PublicModelOk extends SafeForPublic[PublicModel]
  implicit object IterablePublicModelOk extends SafeForPublic[Iterable[PublicModel]]
  implicit object OptionPublicModelOk extends SafeForPublic[Option[PublicModel]]
  implicit object UnitOk extends SafeForPublic[Unit]
}
object Implicits extends LowPriorityImplicits {
  implicit object NothingOk extends SafeForPublic[Nothing]
}
import Implicits._
def boxed[T : SafeForPublic](t: Option[T]) = println("woohoo!")
boxed(None) // compiles! \o/
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top