(Thanks to Dotty Team & Scala Center!)
(Thanks to Scala Center!)
ref: Scala 3 Migration guide · An evolving guide to support the migration to Scala 3
Scala 3の新機能や、非互換性についての詳細は取り扱いません
両方向について扱える
なお、Scala 3でビルドすると.jarの他に.tastyというバイナリを生成する。
Scala 3 バイナリを利用するときはjarではなくTASTyを利用することが、互換性維持戦略の前提になっている。
両方向の互換があることで、Scala 3とScala 2.13を混ぜてことができる。
段階的なmigrationが可能になる。
ref: Scala 3 Migration Guide (continuation) - Scala 3 Release Projects - Scala Contributors
「対応」=「Scala 3から問題なく使える」と定義すると
libraryDependencies += ("a" %% "b" % "c").withDottyCompat(scalaVersion.value)
以下のようなケース
できることとしては
でどうやってScala 3を取り入れる?
domain
adapter
utils
app
domain
adapter
utils
app
lazy val app = project
.enablePlugins(PlayScala)
.dependsOn(adapter, utils)
lazy val adapter = project
.settings(
libraryDependencies +=
"com.typesafe.play" %% "play-json" % "2.9.1"
).dependsOn(domain)
val scala213 = "2.13.3"
ThisBuild / scalaVersion = scala213
lazy val domain = project
lazy val utils = project.dependsOn(adapter)
domain
adapter
utils
app
lazy val app = project
.enablePlugins(PlayScala)
.dependsOn(adapter, utils)
lazy val adapter = project
.settings(
libraryDependencies +=
"com.typesafe.play" %% "play-json" % "2.9.1"
).dependsOn(domain)
// 未リリースのためSNAPSHOT
val scala213 = "2.13.4-bin-d66ebf4"
ThisBuild / scalaVersion = scala213
lazy val domain = project
lazy val utils = project.dependsOn(adapter)
domain
adapter
utils
app
lazy val app = project
.enablePlugins(PlayScala)
.dependsOn(adapter, utils)
lazy val adapter = project
.settings(
libraryDependencies +=
"com.typesafe.play" %% "play-json" % "2.9.1"
).dependsOn(domain)
// 未リリースのためSNAPSHOT
val scala213 = "2.13.4-bin-d66ebf4"
val scala3 = "0.27.0-RC1"
ThisBuild / scalaVersion = scala213
lazy val domain = project
.settings(scalaVersion := scala3)
lazy val utils = project
.settings(scalaVersion := scala3)
.dependsOn(adapter)
package domain
object User {
opaque type Id = Int
opaque type Name = String
object Id {
def apply(i: Int): Id = i
}
object Name {
def apply(n: String): Name = n
}
}
case class User(id: User.Id, name: User.Name)
package adapter
//some imports here...
object UserFormat {
// Failed to read User constructor from Scala 3 TASTy. Why?
// implicit val userFormat: Format[User] = Json.format[User]
// Explicit read codec works fine.
implicit val userReads: Reads[User] = (
(JsPath \ "id").read[Int] and
(JsPath \ "name").read[String]
)(User.apply _)
// Watch out: User.unapply signature has been changed.
implicit val userWrites: Writes[User] = (
(JsPath \ "id").write[Int] and
(JsPath \ "name").write[String]
)(u => (u.id, u.name))
}
package adapter
import domain._
import play.api.libs.json._
case class Post(user: User, content: String)
object Post {
import adapter.UserFormat._
implicit val postFormat: Format[Post] = Json.format[Post]
}
package utils
import domain._
import adapter._
import User._
import UserFormat._
import play.api.libs.json._
object Converter {
// You can use play-json codec here, since this doesn't call macro expansion directly.
def toJson(post: Post): JsValue = Json.toJson(post)
def toJson(user: User): JsValue = Json.toJson(user)
}
package controllers
//some imports here...
@Singleton
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
def index = Action { implicit request: Request[AnyContent] =>
Ok(views.html.index())
}
def hello(id: User.Id, name: User.Name) = Action { implicit request: Request[AnyContent] =>
Ok(Converter.toJson(Post(User(id, name), "Hello, world!")))
}
}