Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @ambiguiousImplicits annotation similar to @implicitNotFound #6806

Closed
scabug opened this issue Dec 12, 2012 · 12 comments
Closed

Add @ambiguiousImplicits annotation similar to @implicitNotFound #6806

scabug opened this issue Dec 12, 2012 · 12 comments
Milestone

Comments

@scabug
Copy link

scabug commented Dec 12, 2012

Ambiguous implicit values can be employed intentionally in order to enforce a compile-time constraint (see for example http://stackoverflow.com/questions/4403906/is-it-possible-in-scala-to-force-the-caller-to-specify-a-type-parameter-for-a-po, in which ambiguity is used to prevent "Nothing" from being inferred as the return type of a method).

However, the compile time error for "ambiguous implicit values" is opaque and not obviously related to the constraint being enforced. There should be a @ambiguousImplicits annotation similar to @implicitNotFound that allows you to provide a user-friendly error message in the case that an implicit search for the annotated type triggers an "ambiguous implicit values" compile error.

@scabug
Copy link
Author

scabug commented Dec 12, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6806?orig=1
Reporter: Eric Pederson (ericacm)
Assignee: @puffnfresh

@scabug
Copy link
Author

scabug commented Dec 12, 2012

Aaron Novstrup (anovstrup) said:
An @ambiguousImplicits annotation would be useful in any situation in which ambiguity is intentionally used to enforce a constraint. However, I'm not sure it should go on the implicit method(s) since that could itself introduce ambiguity. For the use cases I know of, it would be reasonable to put the annotation on the target type. As in

@ambiguousImplicits(msg = "An explicit type parameter is required.")
sealed trait NotNothing[T] { type U }                                          
object NotNothing {
   implicit val nothingIsNothing = new NotNothing[Nothing] { type U = Any }
   implicit def notNothing[T] = new NotNothing[T] { type U = T }           
}

I also don't think that this issue rises to the level of "blocker", but I have no power to edit that field.

@scabug
Copy link
Author

scabug commented Dec 12, 2012

Eric Pederson (ericacm) said (edited on Dec 12, 2012 9:24:23 PM UTC):
Right, that's how @implicitNotFound works too. I've updated the description. Yeah, I agree, blocker is too strong, that was only choice when I logged the bug :)

@scabug
Copy link
Author

scabug commented Dec 12, 2012

Eric Pederson (ericacm) said:
Thanks for the edits

@scabug
Copy link
Author

scabug commented Dec 15, 2012

Eric Pederson (ericacm) said (edited on Dec 15, 2012 1:23:32 AM UTC):
Something related - it would be nice to add a types not equal operator to the other type comparison "operators". Types not equal can be implemented using ambiguous implicits similar to this:

// Courtesy of Miles Sabin.  See http://stackoverflow.com/a/6944070/158658
object TypesNotEqual {
  trait =!=[A, B]
 
  implicit def neq[A, B] : A =!= B = null
 
  // This pair excludes the A =:= B case
  implicit def neqAmbig1[A] : A =!= A = null
  implicit def neqAmbig2[A] : A =!= A = null
}

@scabug
Copy link
Author

scabug commented Jan 16, 2013

@puffnfresh said:
I'm guessing this is going to be possible with Type Macros. Usages of NotNothing will just need to check that a type parameter was not supplied then just use Context#error(pos, "An explicit type parameter is required.").

@scabug
Copy link
Author

scabug commented Jan 16, 2013

@puffnfresh said:
Only part of the way but the following is already possible in Macros Paradise:

import reflect.macros.Context

type NotNothing[A] = macro notNothingImpl[A]
def notNothingImpl[A: c.WeakTypeTag](c: Context) = {
  import c.universe._
  val isNothing = weakTypeOf[A] =:= typeOf[Nothing]
  if (isNothing)
    c.error(NoPosition, "An explicit type parameter is required.")
  TypeTree(weakTypeOf[A])
}

@scabug
Copy link
Author

scabug commented Jan 16, 2013

@retronym said:
Even a def macro in 2.10.1 should handle this: we're likely to backport the Eugene's improvements to def macros to allow polymorphic macros to abide by the usual inference rules.

% scala-hash origin/paradise/macros
[info] downloading http://scala-webapps.epfl.ch/artifacts/a49722990655633c2c97ddf5699adf25bc8bea76/pack.tgz ...done.
[info] scala revision from 2013-01-14 11:39:03 -0800 downloaded to /Users/jason/usr/scala-v2.11.0-M1-80-ga497229
[info] origin/par => /Users/jason/usr/scala-v2.11.0-M1-80-ga497229
Welcome to Scala version 2.11.0-20130114-113903-a497229906 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class NotNothing[T]
defined class NotNothing

scala> object NotNothing {
     |   val NotNothingShared = new NotNothing[Any]
     | } 
defined object NotNothing
warning: previously defined class NotNothing is not a companion to object NotNothing.
Companions must be defined together; you may wish to use :paste mode for this.

scala> import language.experimental.macros
import language.experimental.macros

scala> def impl[A: c.WeakTypeTag](c: reflect.macros.Context): c.Expr[NotNothing[A]] = {
     |   import c.universe._
     |   val isNothing = weakTypeOf[A] =:= typeOf[Nothing]
     |   if (isNothing)
     |     c.error(NoPosition, "An explicit, non-Nothing type parameter is required.")
     |   reify { NotNothing.NotNothingShared.asInstanceOf[NotNothing[A]] }
     | }
impl: [A](c: scala.reflect.macros.Context)(implicit evidence$1: c.WeakTypeTag[A])c.Expr[NotNothing[A]]

scala> implicit def notNothing[A]: NotNothing[A] = macro impl[A]
defined term macro notNothing: [A]=> NotNothing[A]

scala> def foo[A: NotNothing] = ()
foo: [A](implicit evidence$1: NotNothing[A])Unit

scala> foo[Int]

scala> foo[Nothing]
<console>:20: error: could not find implicit value for evidence parameter of type NotNothing[Nothing]
              foo[Nothing]
                 ^
scala> foo
<console>:20: error: could not find implicit value for evidence parameter of type NotNothing[A]
              foo
              ^

@scabug
Copy link
Author

scabug commented Jan 16, 2013

Aaron Novstrup (anovstrup) said:
These macro-based solutions are interesting alternatives to Miles Sabin's ambiguous-implicit-based one (see http://stackoverflow.com/a/4580176/65299) and would be useful contributions to the related Stack Overflow question. However, they don't address the more general issue: ambiguous implicits can be used to enforce type constraints (and may be preferred over macros since macros are advanced language features), but the error messages are uninformative without some mechanism for overriding the default message.

@scabug
Copy link
Author

scabug commented May 7, 2014

Barnabás Králik (kralikba) said:
Dear devs,
What are your comments on this?
Can such a modification break anything? If no, would a pull proposal speed things up?

@scabug
Copy link
Author

scabug commented Sep 7, 2014

@puffnfresh said:
I have a PR for this on the Typelevel fork:

typelevel/scala#32

@scabug scabug closed this as completed Sep 8, 2015
@scabug
Copy link
Author

scabug commented Sep 30, 2015

@som-snytt said:
scala/scala#4673

@scabug scabug added this to the 2.12.0-M3 milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant