Как обмануть Scala, чтобы ничего не находить повторяющиеся имплициты

Я пытаюсь использовать шаблон класса типов в Scala, чтобы пометить все допустимые сериализуемые типы API, чтобы мы могли обеспечить безопасность во время компиляции в отношении того, что мы сериализуем. Наша базовая библиотека принимает AnyRef, что может привести к странным ошибкам, если тип явно не объявляется перед его сериализацией.

Мы разрешаем отправку общедоступной модели, итерации общедоступных моделей, варианта общедоступной модели или единицы.

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]

Этот метод хорошо работает для всего, кроме методов, в которых тип параметра является опцией. Это связано с тем, что None является Option[Nothing], поэтому T = Nothing говорит компилятору искать неявный объект типа SafeForPublic[Nothing], и он найдет как SafeForPublic[PublicModel], так и 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 ><

Любая идея, как я могу обмануть компилятор, чтобы он не находил повторяющиеся имплициты для Nothing. Я видел, как у Майлза Сабина был трюк, использующий:

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 }
}

Но я не мог понять, как его использовать. Халп?


person Jeff May    schedule 17.11.2013    source источник
comment
Взгляните на этот вопрос для ознакомления с приемом отрицания. В итоге я получил общий класс типа Not[_]. Смотрите ответ для более подробной информации.   -  person nadavwr    schedule 18.11.2013


Ответы (1)


Хорошо, благодаря некоторой помощи IRC-канала Scala я понял, что имплициты LowPriority были созданы для решения этой проблемы.

Я использовал это, чтобы исправить это:

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/
person Jeff May    schedule 17.11.2013
comment
Не могли бы вы объяснить, почему это решение работает? Я предполагаю, что имя LowPriorityImplicits не является чем-то особенным, но это также не ясно из вашего ответа. - person ziggystar; 05.11.2015
comment
@ziggystar это специальное правило для наследования имплицитов, ознакомьтесь с scala-lang.org/files/archive/spec/2.11/ - person Jeff May; 21.01.2016