Является ли неоднозначное неявное значение единственным способом, которым мы хотим, чтобы ошибка существовала во время компиляции

trait Foo

trait Bar extends Foo

def doStuff[T <: Foo](x: T)(implicit ev: T =!:= Foo) = x

doStuff(new Foo{}) //ambiguous implicit value
doStuff(new Bar)// successful

Неявное разрешение происходит во время компиляции, поэтому я думаю, что здесь могут быть два неявных значения с одинаковым типом для запуска двусмысленных вещей.

Прямо сейчас я собираюсь ввести в команду shapeless, мои коллеги считают, что этот двусмысленный имплицит не идеален, и у меня нет сильных аргументов по этому поводу. Это единственный способ сделать это, чтобы сделать тип безопасным в scala. Если это так, что я могу сделать, чтобы настроить сообщение об ошибке?

Изменить:

В бесформенном я хочу сделать сумму 2 NAT не равной 7, я могу кодировать так, чтобы компиляция не удалась.

def typeSafeSum[T <: Nat, W <: Nat, R <: Nat](x: T, y: W)
         (implicit sum: Sum.Aux[T, W, R], error: R =:!= _7) = x

typeSafeSum(_3, _4)

но сообщение об ошибке имеет неоднозначное неявное значение, как я могу настроить сообщение об ошибке?


person Xiaohe Dong    schedule 24.07.2014    source источник


Ответы (3)


Простой класс типов был бы лучше, чем проверка неравенства типов в этом (и большинстве других) экземпляров.

Предположительно причина для исключения Foo заключается в том, что Bar (и его братья и сестры) имеют некоторые свойства, которых нет у Foo. Если это так, вам следует создать класс типов, который фиксирует эти свойства, и сделать это требованием аргумента типа для doStuff. Вы можете использовать аннотацию Scala @implicitNotFound, чтобы сделать сообщения об ошибках компилятора более понятными, когда это требование не выполняется.

@annotation.implicitNotFound(msg = "No Props instance for ${T}")
trait Props[T] {
  def wibble(t: T): Double
}

trait Foo
// Note no Props instance for Foo ...

trait Bar extends Foo
object Bar {
  // Props instance for Bar
  implicit def barProps: Props[Bar] = new Props[Bar] {
    def wibble(t: Bar): Double = 23.0
  }
}

def doStuff[T <: Foo](t: T)(implicit props: Props[T]) = props.wibble(t)

scala> doStuff(new Foo {})
<console>:11: error: No Props instance for Foo
              doStuff(new Foo {})
                     ^

scala> doStuff(new Bar {})
res1: Double = 23.0

Если таких свойств, которые отличают Foo от Bar, нет, тогда вам следует усомниться в своем предположении, что вам нужно исключить Foo из doStuff в первую очередь.

Я был бы рад, если бы вы использовали shapeless в своем проекте, но вы должны использовать =!:= (и собственный Scala =:=) только в крайнем случае, если вообще.

person Miles Sabin    schedule 24.07.2014
comment
извините за поздний ответ, я просто редактирую пример NAT из бесформенного, как я могу настроить это сообщение об ошибке? потому что нет @annotation.ambiguousImplicit материала - person Xiaohe Dong; 28.07.2014
comment
Кажется, это совсем другой вопрос. Обычный этикет SO - один вопрос на ... вопрос ;-) - person Miles Sabin; 28.07.2014

В этом другом вопросе я опубликовал ответ, посвященный проблеме получения лучшего сообщения об ошибке: https://stackoverflow.com/a/17047288/1632462

Вот код:

@annotation.implicitNotFound(msg = "Cannot prove that ${A} =!= ${B}.")
trait =!=[A,B]
object =!= {
  class Impl[A, B]
  object Impl {
    implicit def neq[A, B] : A Impl B = null
    implicit def neqAmbig1[A] : A Impl A = null
    implicit def neqAmbig2[A] : A Impl A = null
  }

  implicit def foo[A,B]( implicit e: A Impl B ): A =!= B = null
}

И теперь вы получаете сообщение об ошибке error: Cannot prove that Foo =!= Foo:

def doStuff[T <: Foo](x: T)(implicit ev: T =!= Foo) = x
doStuff(new Foo{}) // error: Cannot prove that Foo =!= Foo
doStuff(new Bar)// successful
person Régis Jean-Gilles    schedule 24.07.2014
comment
Наверное стоило бы добавить это к бесформенным... не могли бы вы прислать мне пиар? - person Miles Sabin; 29.07.2014

Другой способ, которым вы можете наложить ограничение на свой метод doStuff, использовать шаблон магнита, который просто является расширенным классом типов, например:

trait Foo
trait Bar

object DoStuff {
  def doStuff[T](value: StuffMagnet[T]): Unit = value()
}

trait StuffMagnet[T] {
  def apply(): Unit
}

object StuffMagnet {
  implicit def forFoo(foo: Foo): StuffMagnet[Foo] = 
    new StuffMagnet[Foo] {
      def apply(): Unit = ()
    }
}

Разница в этом случае будет заключаться в том, что вы не можете добавить аннотацию implicitNotFound для лучшего сообщения об ошибке времени компиляции, вы получите простое несоответствие типов, например:

Test.scala:20: error: type mismatch;
 found   : Test.Bar
 required: Test.StuffMagnet[?]
  DoStuff.doStuff(new Bar {})
                  ^
one error found
person 4lex1v    schedule 24.07.2014