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:
- Dotty traits and classes, for-loops, if/then, and try/catch
- Enums in Dotty
- Intersection Types in Dotty
- Extension methods in Dotty
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.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
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.