Compiler ignores instance of typeclass and tries to derive it instead
See original GitHub issueRegression found in Open CB #4758 for wangzaixiang/wjson Part of the 3.2.1-RC1 regressions tracker https://github.com/lampepfl/dotty/issues/15949
There seem to be 2 reasons why code now fails to compile:
- In Scala 3.2.0 compiler used defined instance of
given [T:JsValueMapper]: JsValueMapper[Map[String, T]]
in 3.2.1 it tries to derive type class instead usinggiven [T: JsValueMapper, CC[x] <: IterableOps[x, CC, CC[x]]]: Conversion[CC[T], JsArray]
. See AST below - While deriving
Map[String, Int]
TypeTree.of[T].symbol
whereT=Tuple2[String, Int]
returns NoSymbol
Compiler version
3.2.1-RC1
Minimized code
// test.scala
import scala.collection.IterableOps
enum JsValue:
case JsNumber(value: Double|Long)
case JsString(value: String)
case JsArray(elements: Seq[JsValue])
case JsObject(fields: Map[String, JsValue])
object JsValue:
given [T: JsValueMapper]: Conversion[T, JsValue] = ???
given [T: JsValueMapper, CC[x] <: IterableOps[x, CC, CC[x]]]: Conversion[CC[T], JsArray] = ???
trait JsValueMapper[T]:
def toJson(t: T): JsValue
object JsValueMapper:
inline given[T](using deriving.Mirror.ProductOf[T]): JsValueMapper[T] = ${JsValueMapperMacro.generateImpl[T]}
given JsValueMapper[Int] = ???
given JsValueMapper[String] = ???
given [T:JsValueMapper]: JsValueMapper[Map[String, T]] = ???
extension [T: JsValueMapper](obj: T)
inline def toJson: JsValue = summon[JsValueMapper[T]].toJson(obj)
@main def Test =
val works = Map("a"->1,"b"->2,"c"->3).toJson
val fails = Map("a"->1,"b"->2,"c"->3): JsValue // error
// macro.scala
import scala.quoted.*
import scala.deriving.*
object JsValueMapperMacro:
def generateImpl[T: Type](using Quotes): Expr[JsValueMapper[T]] =
import quotes.reflect.*
import JsValueMapper.*
val sym = TypeTree.of[T].symbol // no symbol for Tuple2[String,Int]
val module = Ref(sym.companionModule)
???
Output
[error] java.lang.AssertionError: assertion failed
[error] at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:11)
[error] at scala.quoted.runtime.impl.QuotesImpl$reflect$Ref$.apply(QuotesImpl.scala:435)
[error] at scala.quoted.runtime.impl.QuotesImpl$reflect$Ref$.apply(QuotesImpl.scala:434)
[error] at JsValueMapperMacro$.generateImpl(macro.scala:9)
[error]
[error] val fails = Map("a"->1,"b"->2,"c"->3): JsValue // error
[error] ^
AST in Scala 3.2.0
val fails: wjson.JsValue =
wjson.JsValue.given_Conversion_T_JsValue[Map[String, Int]](
wjson.JsValueMapper.given_JsValueMapper_Map[Int](
wjson.JsValueMapper.given_JsValueMapper_Int
)
).apply(
Map.apply[String, Int](
[ArrowAssoc[String]("a").->[Int](1),
ArrowAssoc[String]("b").->[Int](2)
,ArrowAssoc[String]("c").->[Int](3) : (String, Int)]*
)
):wjson.JsValue
AST in Scala 3.2.1-RC1
val fails: wjson.JsValue =
wjson.JsValue.given_Conversion_CC_JsArray[(String, Int),
scala.collection.immutable.Iterable
](
wjson.JsValueMapper.given_JsValueMapper_T[(String, Int)](
new scala.runtime.TupleMirror(2).$asInstanceOf[
scala.deriving.Mirror.Product{
MirroredMonoType = (String, Int);
MirroredType = (String, Int)
; MirroredLabel = ("Tuple2" : String);
MirroredElemTypes = (String, Int)
; MirroredElemLabels = (("_1" : String), ("_2" : String))
}
]
)
).apply(
Map.apply[String, Int](
[ArrowAssoc[String]("a").->[Int](1),
ArrowAssoc[String]("b").->[Int](2)
,ArrowAssoc[String]("c").->[Int](3) : (String, Int)]*
)
):wjson.JsValue
Expectation
Should compile
Issue Analytics
- State:
- Created a year ago
- Comments:12 (7 by maintainers)
Top Results From Across the Web
How do I write, "if typeclass a, then a is also an instance of b ...
the compiler would be well within its rights to ignore his instance and try to use yours. The right answer, sadly, is to...
Read more >DeriveAnyClass and GND don't work well together - GitLab
It says "the compiler will simply generate an empty instance". This works for all typeclasses, so in a sense this is most general...
Read more >Chapter 6. Using Typeclasses - Real World Haskell
An instance type of this typeclass is any type that implements the functions defined in the typeclass. This typeclass defines one function.
Read more >A somewhat radical proposal for Show · Issue #1675 - GitHub
I propose that we add a type class to the compiler. ... Instead of automatically deriving instances, I think it might be better...
Read more >Type Classes - PureScript by Example
This code declares a type class instance called showBoolean - in PureScript, ... The PureScript compiler will try to infer constrained types when...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@odersky I’m not really the expert here, but I prefer the behavior of 3.2.1 here compared to 3.2.0. implicit search precedence is already really complicated, and one more exception that can change precedence so drastically makes the rules even harder to grasp.
Just in my opinion.
926d5bd1b1d11fdf39db7aaac53584c136aaf1cd is the commit that caused the change between 3.2.0 and 3.2.1. Double checked that its parent commit still behaves the way the author expects, and it does.