Tooling for scala 3

Dotty support in Metals

Tomasz Godzik

Some fun new concepts

 

Besides:

- simplified pattern matching

- better macros

- enums

- focus on ease of migration

- implicit suggestions

- a lot of even more modern features

 

THE FUTURE OR SCALA 3

trait A:
  def f: Int

class C(x: Int) extends A:
  def f = x

object O:
  def f = 3

Optional braces

val x: String = ???
val y: String | Null = ???

x == null       // error
x eq null       // error
"hello" == null // error

y == null       // ok
y == x          // ok

explicit nulls

@main def helloWorld(
  age: Int, 
  name: String, 
  others: String*
) = {
  println(s"Hello $name!")
  println(s"You are $age years old")
}

outer methods

case class Circle(
  x: Double, 
  y: Double, 
  radius: Double
)

def (c: Circle).circumference: Double = 
  c.radius * math.Pi * 2

Extension methods

scalameta parser

- basis for a number of developer's tools

 

- works without the compiler

 

- fast

 

- has an easily understandable AST

 

- a lot projects depending on it

extension (c: Circle) 
   def shapeName = "Circle"
Source(
  stats = List(
    Defn.ExtensionGroup(
      eparam = Term.Param(
        mods = List(),
        name = Term.Name(value = "c"),
        decltpe = Some(value = Type.Name(value = "Circle")),
        default = None
      ),
      tparams = List(),
      uparams = List(),
      body = Defn.Def(
        mods = List(),
        name = Term.Name(value = "shapeName"),
        tparams = List(),
        paramss = List(),
        decltpe = None,
        body = Lit.String(value = "Circle")
      )
    )
  )
)

scalameta parser

enum ListEnum {
    enum ListEnum[+A] {
    case Cons(h: A, t: ListEnum[A])
    case Empty
  }
}

Document outline

Worksheets

scalameta parser

Parser Scala when used with the scala3 dialect

optional braces support

all the new features supported

scalameta parser

scala.meta.parser.dotty.CommunityDottySuite:
From https://github.com/lampepfl/dotty
   a0707985bf..1f503e7868  master        -> origin/master
   e8cfbb381f..45b8dc0f9e  release-3.0.0 -> origin/release-3.0.0
HEAD is now at 13de192d04 Merge pull request #11958 from lampepfl/derived-names-mac
--------------------------
dotty
Files parsed correctly 828
Files errored: 0
Time taken: 14989ms
Lines parsed: ~190k
Parsing speed per 1k lines ===> 78 ms/1klines
--------------------------
  + community-build-dotty 17.179s

scalafmt

- important to keep consistent style for a team

- runs on CI

- can be considered a blocker to Scala 3 migration

object Main:

  def main(args: Array[String]) =
    val name: Option[String] = Some("AVeryLongString")
      name 
       match    
         case None => println("Hello <unknown>")
          case Some(x) => println(s"Hello $x")
object Main:

  def main(args: Array[String]) =
     val name: Option[String] = 
        Some("AVeryLongString")
     name 
        match    
           case None    => println("Hello <unknown>")
           case Some(x) => println(s"Hello $x")

scalafmt

version = 3.0.0-RC1
runner.dialect = scala3
indent.main = 3

scalafmt

version = 3.0.0-RC1

fileOverride {
  "glob:**/scala-3*/**" {
    runner.dialect = scala3
    indent.main = 3
  }
}

scalafmt


def hello(name: String): Unit = {
  val greeting = s"Hello $name"
  println(greeting)
}

Rewrite - drop braces


def hello(name: String): Unit =
  val greeting = s"Hello $name"
  println(greeting)

scalafmt

import scala.meta.{Main => Metals}
import scala.meta._

object Main{}

Rewrite - New syntax

import scala.meta.Main as Metals
import scala.meta.*

object Main{}

mdoc

```scala mdoc
val x = 1
val y = 2
x + y
```
```scala
val x = 1
// x: Int = 1
val y = 2
// y: Int = 2
x + y
// res0: Int = 3
```

after

Before

mdoc

```scala mdoc:crash
val y = ???
```
```scala
val y = ???
// scala.NotImplementedError: an implementation is missing
//  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:288)
//  at repl.MdocSession$App$$anonfun$1.apply$mcV$sp(modifiers.md:9)
//  at repl.MdocSession$App$$anonfun$1.apply(modifiers.md:8)
//  at repl.MdocSession$App$$anonfun$1.apply(modifiers.md:8)
```

after

Before

mdoc

```scala mdoc:scalafmt
indent.callSite = 2
---
function(
argument1, // indented by 2
""
)
```
```scala formatted
function(
  argument1, // indented by 2
  ""
)
```
```scala original
function(
argument1, // indented by 2
""
)
```
<details class='config' open><summary>Config for this example:</summary><p>
```scala config
continuationIndent.callSite = 2
```
</p></details>

after

Before

mdoc

scaladoc

Typechecked scaladoc snippets

```scala sc:failing
val iAmWrong: Int = 2.3
for(i ← 1.to(10))
yield i*2
```

Borrows ideas and code from mdoc

But inside the compile

scalafix

import scala.List
import scala.collection.{immutable, mutable}
object Foo { immutable.Seq.empty[Int] }
import scala.collection.immutable
object Foo { immutable.Seq.empty[Int] }

before

after

scalafix

scalafix

import scala.meta._
import scala.collection.{mutable => mut}

object Main {}
import scala.meta.*
import scala.collection.mutable as mut

object Main {}

before

after

Your favourite language in you favourite Editor

Metals

Scala 3 support

Metals

Scala 3 Inline

Metals

inline def power(x: Double, n: Int): Double =
   if n == 0 then 1.0
   else if n == 1 then x
   else
      val y = power(x, n / 2)
      if n % 2 == 0 then y * y else y * y * x

Scala 3 auto importing implicits

Metals

Tasty!

Metals

         +-------------+    +-------------+    +-------------+
$ scalac | Hello.scala | -> | Hello.tasty | -> | Hello.class |
         +-------------+    +-------------+    +-------------+
                ^                  ^                  ^
                |                  |                  |
           Your source        TASTy file         Class file
               code           for scalac        for the JVM
                              (contains         (incomplete
                               complete         information)
                             information)

MAcros

Metals

import scala.quoted.*

inline def assert(inline expr: Boolean): Unit =
   ${ assertImpl('expr) }

def assertImpl(expr: Expr[Boolean])(using Quotes) = '{
   if !$expr then
      throw AssertionError(s"failed assertion: ${${ showExpr(expr) }}")
}

def showExpr(expr: Expr[Boolean])(using Quotes): Expr[String] =
   '{ "<some source code>" } // Better implementation later in this document

ScalaJS

ScalaJS

currently Implemented inside Scala 3 compiler

reuses most of existing code

Implemented in 3 months!

not outstanding issues

Scala native

Scala native

Windows support

multithreading

scala 3

Scala 3 will be amazing!

Question to Martin

What new tooling feature would you love to see implemented when it comes to Scala 3, but that did not exist in Scala 2?

 

Release party

By Tomek Godzik

Release party

  • 141