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.
This post is sponsored by my new book,
Learn Functional Programming Without Fear.
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 rm
command:
> ./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 - quit
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 App
is where the action starts.- As its name implies,
mainLoop
is the application’s main loop. It runs until you enterq
. - This line of code in the loop converts the user’s input into a
Command
:scala cmd <- getLine.map(Command.parse _)
- Commands are then processed in the
processCommand
method.
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.”
Scalaz’s unsafePerformIO
For a little more background, the Scaladoc for unsafePerformIO
states:
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:
rm foo
instead of this:
rm 1
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.)
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.
GraalVM
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 todo
.
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
JAVA_HOME
andPATH
environment variables to use Graal cd
into 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.,
. 1setup_graal
) - After I run
sbt-assembly
, I then run the 2compile_graal.sh script to create thetodo
native 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.
Summary
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.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
All the best,
Al