A small Scala 2 project converted to Dotty (Scala 3)

If you want to see a somewhat larger example of Dotty source code than what I’ve shown before, I just took a little time to convert a small Scala 2 project over to the current Dotty syntax (i.e., the Dotty syntax supported by the Dotty 0.21.0-RC1 release, circa January, 2020).

There’s nothing too mind-blowing in this blog post (or in the entire project), but I wanted to share some the project code to give you a taste of what Dotty code currently looks like.

As a bit of background, all of the source code is from a Scala/JavaFX “Notes” application I wrote with Scala 2.12 a few years ago. The project is pretty small, consisting of 17 source code files and about 450 lines of code. You can look at all of the code here on Github, or just look at the selected source code files below.

Example 1: Many functions in one file, few `new` keywords

First up, here’s some code from a file named MainGridPaneUtils.scala. The reason I’m showing it is because it shows is a large number of methods in one file:

object MainGridPaneUtils

    def configureGridPaneGeometry(gridPane: MainGridPane): Unit =
        gridPane.setPadding(new Insets(10))
        gridPane.setHgap(10)
        gridPane.setVgap(10)

    def addWidgetsToGridPane(
        gridPane: MainGridPane,
        tableView: TableView[Note],
        topHbox: HBox,
        bottomHbox: HBox
    ): Unit =
        gridPane.add(topHbox,    0, 0)
        gridPane.add(tableView,  0, 1)
        gridPane.add(bottomHbox, 0, 2)

    def configureGridPaneColumnConstraints(gridPane: MainGridPane): Unit =
        val col1 = new ColumnConstraints
        col1.setPercentWidth(100)
        gridPane.getColumnConstraints.addAll(col1)

    def buildUrlColumn(): TableColumn[Note, String] =
        val urlColumn = TableColumn[Note, String](URL_HEADER)
        urlColumn.setMinWidth(200)
        urlColumn.setCellValueFactory(PropertyValueFactory[Note,String]("Url"))
        urlColumn

    def buildTagsColumn(): TableColumn[Note, String] =
        val tagsColumn = TableColumn[Note, String](TAGS_HEADER)
        tagsColumn.setMinWidth(150)
        tagsColumn.setCellValueFactory(PropertyValueFactory[Note,String]("Tags"))
        tagsColumn

    def configureBottomHBoxGeometry(bottomHbox: HBox): Unit =
        bottomHbox.setAlignment(Pos.BASELINE_CENTER)
        HBox.setHgrow(bottomHbox, Priority.ALWAYS)

    def addWidgetsToBottomHbox(
        bottomHbox: HBox,
        addNoteButton: Button,
        deleteNoteButton: Button
    ): Unit =
        bottomHbox.getChildren.addAll(addNoteButton, deleteNoteButton)

I deleted some functions in that file so you don’t have to look at all of it. The main points are (a) seeing what a lot of functions in one file looks like without all of the curly braces, and (b) if you look carefully, you’ll see that I deleted a lot of new keywords that are required with Scala 2.

Bonus: Dotty 0.22

It looks like in the next Dotty release (0.22) you’ll be able to use an end marker to end/close your functions, if you prefer. See the “End marker” section of this Dotty page for more details. It looks like it’s only recommended to clarify the end position of long functions and classes, but in theory you could write code like this:

def plus1(i: Int): Int
    i + 1
end plus1

Example 2: if/then syntax

Second, here’s some code from a file named EditNotePane.scala that demonstrates the new if/then syntax:

class EditNotePane extends GridPane

    // deleted a bunch of code here ...

    notesField.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler[KeyEvent]() {
        override def handle(event: KeyEvent): Unit =
            if event.getCode == KeyCode.TAB then
                val node = event.getSource.asInstanceOf[Node]
                val skin = node.asInstanceOf[TextArea].getSkin.asInstanceOf[TextAreaSkin]
                skin.getBehavior.traverseNext
                event.consume
    })

As a point of comparison, a Scala 2 if/then expression would begin like this:

if (event.getCode == KeyCode.TAB) { ...

Example 3: Dotty for-expressions

Finally, here’s some code from a file named Database.scala that demonstrates some for-expressions without curly braces:

class Database

    private val DELIMITER = "‡"

    val dataStore = DataStore(
        Globals.DB_FILE,
        delimiter = DELIMITER,
        newlineSymbol = "«"
    )

    def save(n: Note): Unit =
        val s = convertNoteToPipedString(n)
        dataStore.add(s)

    def delete(n: Note): Unit =
        val s = convertNoteToPipedString(n)
        dataStore.remove(s)

    def getAll(): Seq[Note] =
        val records: Seq[Seq[String]] = dataStore.getAllItemsSeparatedIntoColumns()
        val notes = 
            for
                Seq(note,url,tags,dateCreated,dateUpdated) <- records
            yield
                createNoteFromDatabaseRec(note,url,tags,dateCreated,dateUpdated)
        NoteUtils.populateShortenedNotes(notes)

    def getAllFullTextSearch(searchFor: String): Seq[Note] =
        val records: Seq[Seq[String]] = dataStore.getAllItemsSeparatedIntoColumns()
        val notes = 
            for
                rec <- records
                if anyFieldContainsString(rec, searchFor)
            yield 
                createNoteFromDatabaseRec(rec(0),rec(1),rec(2),rec(3),rec(4))
        NoteUtils.populateShortenedNotes(notes)

    def getAllByTag(searchFor: String): Seq[Note] =
        val records: Seq[Seq[String]] = dataStore.getAllItemsSeparatedIntoColumns()
        val notes = 
            for
                rec <- records //Seq[String]
                if rec(2) contains(searchFor)
            yield 
                createNoteFromDatabaseRec(rec(0),rec(1),rec(2),rec(3),rec(4))
        NoteUtils.populateShortenedNotes(notes)

More details

If you want to see the fully-converted project, it’s available at this Github URL:

As I mentioned before, the project consists of 17 source code files and about 450 lines of code.

More Dotty examples

If you want to see more Dotty examples I’ve created, see these links:

How I converted that project from Scala 2 to Dotty

For those interested in how I converted that project from Scala 2 to Dotty, this is what I did:

  • Installed Dotty 0.21 with Homebrew
  • Created a new Dotty/SBT project
  • Copied the source code and one library (jar file) from the original project into this new project
  • Tried to compile the project inside SBT, got a few errors
  • Fixed the errors, mostly related to the new procedure syntax
  • Commented-out a couple of errors that I didn’t care to investigate now
  • Got the project to compile inside SBT
  • Created a list of *.scala source code files in the project
  • Converted them to the new Dotty “significant indentation” syntax using the script below
  • Made a few more changes to the files by hand, mostly (a) removing new keywords and (b) updating the for-expressions

Shell script I used to convert the code from Scala 2 to Dotty

I created a file named scala_files that contains a list of all of the *.scala files in the project, and then created this shell script to let dotc convert all of those files for me:

for i in `cat scala_files`
do
    echo "compiling $i ..."
    dotc -indent -rewrite -classpath "target/scala-0.21/classes:lib/flatfiledatabase_2.12-0.5.jar" $i
done

There may be other ways to do this, but the keys for me were:

  • Compiling the initial code with SBT to create the _.class files in target/scala-0.21/classes
  • Configuring the CLASSPATH as shown to make dotc happy

A key thing to note about this approach is that it wouldn’t work well if your project has a number of managed dependencies (since I build the CLASSPATH manually). Technically you could do this if there’s a way to dump a list of all the managed dependencies in SBT, and it looks like there’s a potential SBT plugin to do that.

That being said, I’m sure there are other ways to attempt to convert projects that I’m not aware of yet — maybe Scalafix or others — and if there aren’t yet, there certainly will be soon.

A slightly better conversion process

Based on this Dotty page, it looks like I should have run two dotc commands back to back in my shell script:

FIRST:  dotc -rewrite -new-syntax
SECOND: dotc -rewrite-indent

I’m not going to redo this project, but if I try this again on a second project I’ll use those two steps.

Summary

In summary, if you wanted to see a somewhat larger example of Dotty source code that I’ve shown previously, I hope this is helpful.