Back when I was writing Functional Programming, Simplified I started to write a little Scala/FP “To-Do List” application that you can run from the command line, based on a similar application in the Learn You A Haskell For Great Good book. For reasons I don’t remember, I decided not to include it in the book, and forgot about it until I started using GraalVM (“Graal”) recently.
Graal includes a native image feature lets you compile JVM classes and JAR files into native executables, so as I thought about things I can make faster, I was reminded of the To-Do List app and thought about how cool it would be if it started instantaneously. So I found the old project, blew the dust off of it (updated all of its dependencies), and made a few additions so I could create (a) a single, executable JAR file with sbt-assembly, and (b) a native executable with Graal.
Because I wrote the application for my book, I wrote the code using Scala in a functional programming (FP) style. If you read the book, the code should look very familiar, with the main exception being that I use some tools from the scalaz project. (It looks like I also have another version that I wrote with Cats.)
UPDATE: For an improved version of this code, see my newer article, A Scala functional programming style To-Do List written with Cats and Cats-Effect.Back to top
How the application works
As mentioned, this is a command-line To-Do List application, and here’s a short demonstration of how it, starting with the
v command (which stands for “view”) to show that there’s nothing in the initial list, followed by some
add commands and a
> ./todo Command ('h' for help, 'q' to quit) ==> v Command ('h' for help, 'q' to quit) ==> add wake up 1. wake up Command ('h' for help, 'q' to quit) ==> add make coffee 1. wake up 2. make coffee Command ('h' for help, 'q' to quit) ==> add go to work 1. wake up 2. make coffee 3. go to work Command ('h' for help, 'q' to quit) ==> rm 3 1. wake up 2. make coffee Command ('h' for help, 'q' to quit) ==> h Possible commands ----------------- add <task> - add a to-do item h - show this help text rm [task number] - remove a task by its number v - view the list of tasks q - quitBack to top
The Scala/FP code
If you’re interested in functional programming with Scala, the source code is available at this URL:
Here’s a short overview of how the code works:
ToDoListFIO extends Appis where the action starts.
- As its name implies,
mainLoopis the application’s main loop. It runs until you enter
- This line of code in the loop converts the user’s input into a
scala cmd <- getLine.map(Command.parse _)
- Commands are then processed in the
Something new you’ll see in the code (compared to my book) is a call to Scalaz’s
unsafePerformIO function. As one person wrote, that is an “Intentionally verbose and scary name to make you think twice about where you perform your side effects.”
For a little more background, the Scaladoc for
Runs I/O and performs side-effects. An unsafe operation. Do not call until the end of the universe.
Another way to state that is that with Scalaz you write all of your I/O code using its IO monad, and then make a single call to
unsafePerformIO when you’re ready to trigger the execution of your application. In fact, if you don’t call
unsafePerformIO, your application won’t do anything. (Side effects are a very big deal in functional programming, hence this scary method name.)
For more information about it, if you search this Eric Torreborre page for
unsafePerformIO, you’ll find quite a few comments about it. The first part of this page also shows a short example of how it triggers the execution of a very small piece of code. I also show a short example of it in this A Scalaz putStrLn “Hello, world” IO Monad example.
Monday morning update: I woke up during the night and remembered that the reason I never posted this code before is that it has too many calls to
unsafePerformIO, and I meant to dig more into Scalaz to learn how to improve the code. I have to travel a lot early this week, but I’ll try to post an improved version of the code by next weekend.
Hopefully the rest makes sense
If you read my FP book, hopefully the rest of the code will make sense. All of the I/O functions use an
IO monad, and the rest of the code consists of pure functions.
Two bugs (oops)
Right as I was getting ready to go to press tonight I noticed that the code has two bugs.
First, if the todo.dat file doesn’t exist, the “view” command will throw an exception. (You can create an empty initial file to avoid that bug, or fix the code.)
Second, I noticed that I hadn’t fixed a bug related to the “remove” process. If you type in something like this:
instead of this:
the application will blow up. The
remove function assumes that it receives an
Int, but I don’t do anything to validate the user input.
I was going to fix these bugs, but I’m short on time, so I thought I’d leave them as an exercise for the interested reader. (My apologies that I’m a little rushed tonight.)Back to top
Creating a single, executable JAR file
To create a single, executable JAR file, use the
sbt assembly command, or use the
assembly command at the SBT prompt. That command should “just work” for you, but if you need more information, I wrote this sbt-assembly tutorial as part of the Scala Cookbook.
As mentioned, I got back into this project because of Graal. Over the last few weeks I’ve been updating some of my JVM-based shell scripts to use Graal so they’ll start faster, and for however my brain works, it triggered a, “Hey, you wrote a To-Do List app a few years ago” thought. Indeed, using Graal, the app starts up immediately after you type
If you haven’t used Graal yet it takes a little work to set it up initially. Here’s a very short description of what it takes to create a native executable with Graal for this project:
- Install GraalVM on your system
- Configure your
PATHenvironment variables to use Graal
cdinto this project’s Graal directory
- In my case I don’t use Graal all the time, so I open a new MacOS Terminal tab where I only use Graal, then source the 1setup_graal file to set the necessary GraalVM parameters (i.e.,
- After I run
sbt-assembly, I then run the 2compile_graal.sh script to create the
todonative image (executable)
While the app starts pretty fast with the
java-jar command, it starts instantly when you create the Graal native image, and that’s a really cool feeling, especially if you’ve been sadly used to that initial Java/JVM startup lag time for 20 years.
In summary, if you wanted to see how to write a small but complete functional programming application in Scala — or if you just wanted a command-line To-Do List application — I hope this source code and project is helpful.
Finally, as a bit of self-promotion, if that code is hard to understand, please see my Functional Programming, Simplified book. About 40% of the book is available as a free preview, so hopefully you can get a good feel about whether or not you want to buy the book from that preview.
All the best,