com-lihaoyi / scalatags

ScalaTags is a small XML/HTML construction library for Scala.
https://com-lihaoyi.github.io/scalatags/
MIT License
757 stars 117 forks source link

Example doesn't work in Scala 3 with latest versions and sbt #231

Open yatesco opened 2 years ago

yatesco commented 2 years ago

Hi there - I copied your example from cask/scalatags but used the latest versions and Scala 3 and I'm getting a spurious error:

Found:    scalatags.Text.all.html.Self
Required: scalatags.text.Frag

The following import might make progress towards fixing the problem:

  import sourcecode.Text.generate

      html(

build.sbt:

val scala3Version = "3.1.0"

lazy val root = project
  .in(file("."))
  .settings(
    name := "Pride and Joy",
    version := "0.1.0-SNAPSHOT",

    scalaVersion := scala3Version,

    libraryDependencies += "com.lihaoyi" % "cask_3" % "0.8.0",
    libraryDependencies += "com.lihaoyi" % "requests_3" % "0.7.0",
    libraryDependencies += "com.lihaoyi" % "scalatags_3" % "0.11.0",
    libraryDependencies += "com.lihaoyi" % "utest_3" % "0.7.10" % "test",
    libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
  )

src/main/scala/Main:

//package app
import scalatags.Text.all._

object Main extends cask.MainRoutes{
  @cask.get("/")
  def hello() = {
    doctype("html")(
      html(
        body(
          h1("Hello World"),
          p("I am cow")
        )
      )
    )
  }

  initialize()
}

Is this something obvious, or should I stick with the older versions used in the demo?

Thanks!

seoethereal commented 2 years ago

I have also encountered this problem and it seems to be a regression in scalatags because it works fine on 2.13.x versions but not on 3.x.x versions. It seems that the protected[this] qualifier is no longer permitted in Scala 3 which might be related to this problem. From my admittedly limited time of debugging, the Self type inside scalatags.Text.TypedTag has issues with variance which is getting ignored by the @uncheckedVariance annotation. For Scala 2, the scalatags.text.TagFactory#tag function used to create tags such as html, a and so forth refines to ConcreteHtmlTag[String] but in Scala 3, it refines into <tag-name>.Self where <tag-name> is the name of a tag like html or a. This results in the type checking error that you have seen where Self cannot be resolved to text.Frag.

Since doctype is only present in the text packages, I do not think that this issue occurs in the js-dom packages however everything else related to variance most likely does...but I am not fully sure. Here is a slightly smaller reproducible example using mill as the build tool:

build.sc

import mill._
import mill.scalalib._

object example extends ScalaModule {
  // Replace this with Scala 2 or Scala 3 to view the difference.
  override def scalaVersion = "3.0.2"
  override def scalacOptions = T {
    super.scalacOptions() ++
      if (isScala3(scalaVersion)) {
        Seq("-explain")
      } else {
        Seq()
      }
  } 
  override def ivyDeps = T {
    super.ivyDeps() ++ Agg(
      ivy"com.lihaoyi::scalatags:0.11.0"
    )
  }
}

example/src/Example.scala

package example

import scalatags.Text.all._

object Example {
  def main(args: Array[String]): Unit =
    println("hello world")

  // Fails in Scala 3.
  def example() =
    doctype("html")(
      html()
    )
}
yatesco commented 2 years ago

(nice reduction and investigation - thanks)

edwardcwang commented 2 years ago

Could you work around it by using asInstanceOf?

seoethereal commented 2 years ago

@edwardcwang sorry for the late reply but from what I remember, asInstanceOf worked in some places but it does not work as general solution. I only managed to realise that annotating the types explicitly worked because of some trial and error.