Is ambiguous implicit value the only way we want to make the error existed in compilation time

二次信任 提交于 2019-12-03 16:01:19

A simple type class would be better than a type inequality test in this (and most other) instance(s).

Presumably the reason for wanting to exclude Foo is that Bar (and its siblings) have some properties which Foo lacks. If that's the case then you should create a type class which captures those properties and make that a requirement on the type argument for doStuff. You can use Scala's @implicitNotFound annotation to make compiler error messages more comprehensible whenever that requirement isn't met.

@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

If there aren't any such properties which distinguish Foo from Bar then you should question your supposition that you need to exclude Foo from doStuff in the first place.

I'd be delighted if you use shapeless in your project, but you should use =!:= (and Scala's own =:=) only as a last resort, if at all.

Régis Jean-Gilles

In this other question, I posted an answer that adress the issue of getting a better error message: https://stackoverflow.com/a/17047288/1632462

Here's the code:

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

And now you get the error message 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

Another way you can put constraint on your doStuff method use magnet pattern, which is simply enhanced typeclass, e.g:

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 = ()
    }
}

The difference in this case would be that you can't add implicitNotFound annotation for better compile time error message, you'll get a simple type mismatch, like:

Test.scala:20: error: type mismatch;
 found   : Test.Bar
 required: Test.StuffMagnet[?]
  DoStuff.doStuff(new Bar {})
                  ^
one error found
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!