question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Compiler ignores instance of typeclass and tries to derive it instead

See original GitHub issue

Regression 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:

  1. 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 using given [T: JsValueMapper, CC[x] <: IterableOps[x, CC, CC[x]]]: Conversion[CC[T], JsArray]. See AST below
  2. While deriving Map[String, Int] TypeTree.of[T].symbol where T=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:closed
  • Created a year ago
  • Comments:12 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
markehammonscommented, Nov 9, 2022

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

1reaction
markehammonscommented, Nov 9, 2022

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.

Read more comments on GitHub >

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

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found