Working Review of "Practical ML Programming with SML#" (Ohori, Ueno), CHAPTER 6
Techniques of designing and developing ML-style systems
2023-09-30
This chapter is a really fun one to read and implement. It utilizes all of the concepts introduced in chapters 1 through 5, with the goal of creating a standalone program. The program in question is a tty-based game of Reversi.
A high-level breakdown of the chapter
The authors guide us in the design and implemenation of our game in three broad movements:
- Analyzing and designing the system using types
- Top-down implementation
- Considering the procedural and the declarative separately
Analyzing and designing the system using types
The first section shows us how to “think in types” to represent the domain of Reversi. The authors stay close to the KISS philosophy and select the most straightforward datastructures, such as the simple algebraic
datatype color = BLACK | WHITE
to represent the colour of pieces on the board, and a simple property list of
((x,y), color)
tuples to represent their positions. There is a short
discussion of the efficiency of different interger/word representations, but
ultimately the plain vanilla int
is decided upon, for clarity’s sake.
Then, considering only the types determined above, the authors sketch a high-level picture of how the various elements will ‘click together’ to model a game of Reversi.
Top-down implementation
The section titled “Top-down implementation” is an elegant demonstration of how to lean on the type system to guide us in writing concrete code. We start out by slowly fleshing out the “sketch” functions from the previous section so that they match their type signatures, leaving “notes” in place of missing lower-level functionality.
Then, we move on to making the “notes” into real code. We get to see things
like the option
type and various list operations (such as concat
or
filter
) in a real-life program. By the end of this section, we have a
complete “pure code” model of Reversi, which we can play around with in an
interactive SML# session. But no stand-alone program exists yet.
While this section is very informative and unfolds in a nice logical progression, following the top-down methodology the authors advertise, I found two things lacking:
-
The comment → code approach of drilling down into details doesn’t really allow for partially-written functions to compile. This weakens the didactic effect, as it makes it seem that the entire module comes out fully-formed out of Zeus’ proverbial head.
-
Modeling the game as pure code is a boon for testing. I feel that this chapter was edited for brevity, with some kind of interactive-shell-based testing section removed. (We don’t see the
play
function in action!) It would have been even better if the built-in smlunit framework was used to build the pure model in a test-driven manner. Maybe I’ll revisit this idea later on.
Considering the procedural and the declarative separately
Here is where we get to wrap our decalarative core in a procedural outer shell, producing a stand-alone executable that plays Reversi. Again, as in the first section, the authors opt for the simplest possible implementation of their ideas, and the result is both elegant and understandable. It’s quite literally a textbook implementation of the pure/impure sandwich.
Some advanced discussion topics I’d like the authors to have explored:
-
There is a lot of
XfromString
/XtoString
functions in the main module. Would it make sense to extract these into a View (as in ModelViewController)? -
There is a hard-coded dependency on the internals of
Game
in the main file. How could we use the type system to make the dependecy structure looser? In particular, if we’re going to implement alternative (graphical) frontends for the game, how could we use the module system to help us? Would the MVC pattern help?
Exercises
The exercises in this chapter are quite deep. Starting with improving the bare-bones TTY interface, through implementing a CPU opponent, all the way up to implementing a GUI for the game (using a later chapter as reference).
I have a working implementation of the game, including a super-crude CPU opponent, at pzel/reversi. Please check it out.