2024 December Adventure
Thanks to some encouragement from devine lu linvega, I have decided to take part in this year’s December Adventure.
Initial Goals
For my project, I will be looking into Guile Hoot. My goals are to get acquainted with Hoot and its ecosystem as a way to learn more about Web Assembly and Guile itself. A concrete objective is to implement tic-tac-toe using Hoot
. Stretch objective: use Fibers
to code events as data.
I also might stray to consider other Guile topics such as reading Software Design for Flexibility.
Adventure Recap
The goal was to learn more about Guile and WebAssembly. I think I have learned a ton about how to write Guile targetting the browser. The tooling is fantastic and provides a nice entry point to learn WebAssembly by hand-coding it. But I think we’re not there in terms of having a reliable platform for writing Scheme for the browser using green threads, which is what I found most exciting. There is a new release of Hoot
mid-January 2025. I am hoping that I will be able to reengage with this project then.
Writing using bleeding-edge tools have resulted in my doubling down on writing artisanal software, which for me means writing software on a human scale. Some of the issues with the software come from a lack of resources–Spritely is a very small outfit. I can understand their processes and their goals. I can relate to these folks.
Guile is also at a point where it has a great deal of promise: it is use for Guix
, and the Spritely stack paints a vision for a vertically integrated open source stack with considerable reach given Guile’s excellent binidngs to C.
Final thought: I liked the idea of writing software for myself, with no requirement to keep up with professional tools or trends. Being a Scheme, Guile offers really exciting computational vistas like continuations which are not new but not available in most languages. There is also the educational context–lots of stuff for Scheme besides the Little Books series.
December 25:
Goal: Figure out random bugs about too much recursion
I am getting strange browser errors when invoking functions with deterministic output over specific inputs, mostly about recursion when I am only using map
and filter. Moreover, the functions work perfectly well when I run them on the Guile interpreter. An additional data point is that I see the same bug using console.log.
I am at blocked from further progress which is frustrating but I have learned a ton about Guile Hoot
and Fibers
.
Time to take a break.
December 24
Goal: Implement events as data
Run into an issue with Hoot
and Fibers
where fibers will run out of stack frames when invoking a function like car
or cdr
. Eventually found the same issue when invoking console-log
, a function that maps back to console.log
in JS.
Hoping that Spritely folks will have some perspective.
December 23
Goal: Visit with guests
Walked along Dallas Road, saw some Harbour Seals, and ate some fine salmon roasted with Herbs de Provence.
December 22
Goal: Visit with guests
Roasted a couple of chickens and spent really good time with J. Watt.
December 21
Goal: Clean house for guests.
Day off–need to clean the house
December 20
Goal: Figure out how to code events and state
Figured out how to code events and how to deal with state, including all the support functions. Also settled on an architecture that renders in exactly one place in the code.
December 19
Goal: Get asynchronous read/write from a channel working in the browser
Finally got the inbox
code from Spritely working properly on the browser. The big issue today was realising that Guile’s display
function was automatically mapped to console.log
in the browser. My attempt to map to the browser function myself was creating all kinds of confusion.
To recap: I can get Fibers running on the browser and spawn threads that print to the console by putting and reading messages in the enqueue and dequeue channels.
This means that I now can have a Fiber listening for events on the dequeue channel and dispatch them; click events will result in a message being put onto the enqueue channel
December 18
Goal: Get the promise code running in the browser
Tried for a couple of hours but didn’t make any progress.
December 17
Goal: Get the promise code running in the browser
I was able to figure out how to make this happen by modifying the JS code that loads the Wasm module following the instructions in the Hoot tutorial. I can get text to the browser console but I am unable to spawn threads and send messages successfully.
December 16
Goal: Get the proof-of-concept working in the browser.
Tried mightily to get the code running in the browser to no avail. Finally, I posted a message in the Spritely forums and got some hints for how to go about it. I had not understood the loading process completely and was failing to start the promise for Fibers.
December 15
Goal: Get the proof-of-concept code running under Fibers
Working with bleeding edge software is fun but has challenges. One of them is that RTFM is not enough: one has to dip into the code because alpha-quality code is not likely to have complete documentation. The POC code that I wrote a couple of days ago now runs under regular Guile and Fibers and, with modifications to a couple of lines, also under Hoot (different entry points for Fibers).
Independently from the above, I also got events firing correctly for my tic-tac-toe game.
Feeling encouraged after trying many things in order to find the right spells.
December 14
Took the day off.
December 13
Goal: Figure out how make the rendering code work with the event queue
The first step is to get the asynchronous queues compiling under Hoot
. As it turns out, I ran into a bug where a while
loop produced a compilation error. The work-around is to code the loop as a recursive loop.
The working hypothesis now is to have run-fibers
with a thunk that sets up an event loop with three types of events: start, which triggers the initial rendering; move which inserts a token into the square onto which the player has clicked with the mouse and then renders the game board; and history, which resets the board to the state of the history button clicked.
Note that the render
procedure running as part of the run-fibers
thunk will result in the sending of events to the event dispatch procedure. I still need to figure out how to make the queue channels available to the HTML element event handlers. These might need to be passed explicitly by template
, which will get them from render
.
A much nicer way to do have the queue channels available might be to define template
inside the block. What I would have really liked to do would be to have inbox
return a closure for its event loop, the queue channels, and the stop?
operation by using Scheme’s values
.
December 12
Goal: Use Fibers
to wire an event channel
Refactor event queue so that I now have a function (run-code my-fn)
, which takes my-fn
, a function with two queues and a Fibers
conditional variable, corresponding respectively to a queue that can receive events, another queue that will provide events to a listener on the queue, and a variable that we can use to shut down the queues.
In practical terms, this means that UI events will spawn a thread that will place an event (data) onto enq-ch
channel; the event dispatch code will be listening for events on the deq-ch
channel in a loop and will change the global state reflecting the event received.
December 11
Goal: Use Fibers
to wire an event channel
Attended Office Hours held by Spritely Institute. These folks where immensely helpful and shared code that implements asynchronous reads and writes using Fibers
channels. Already recast this code a bit to get it running successfully in my environment. I am very excited to put all the pieces together for my tic-tac-toe game using Hoot
.
December 10
Goal: Use Fibers
to wire an event channel
Spent a bunch of time trying to understand how to set up a data-driven event system using Fibers
and Hoot
. Progress was made–I figure out how to work with channels, including having non-blocking reading of messages. Still struggling on how to put all the pieces together and how to code the event loop. Also, some unexplained behaviour when sending data to Standard Out.
December 9
Goal: Look at tic-tac-toe and map out the work that needs to happen.
My simple version of tic-tac-toe consists of a grid with 9 squares. The first player can click on any square, which will then display an X
. The other player can click on any other square, with an O
being assigned to that square. The first player takes its turn, again clicking on an unoccupied square, resulting on an X
being assigned to the square, and so on. The game is over if a player gets 3 symbols in a row, including the diagonals, or if the squares have been exhausted.
The implemententation requires us to display a grid with the squares, the ability to mark a square with a symbol, allow for moves only on open squares, and logic to decide whether the game can continue.
Today I was able to display the grid with values.
December 8
Rest.
December 7
Goal: Explore Wasm ideas through the Wasm tooling in Hoot
Finally able to play and experiment with some of the code from the tutorial:
scheme@(guile-user)> ,use (hoot compile)
scheme@(guile-user)> (define hello (compile "hello, world"))
scheme@(guile-user)> ,use (wasm types)
scheme@(guile-user)> (wasm? hello)
$7 = #t
In the snippet above, we have defined hello
, a Wasm module, and were able to ascertain that, in fact, hello
is a Wasm module through the invocation of the wasm?
procedure.
Lots more in the tooling section of the tutorial for Hoot
.
December 6
Goal: Get a good REPL workflow.
Read a bunch of the WebAssembly specification to get a sense for the various elements needed for Hoot
. Now that I have a feeling for how the pieces fit, the next thing to do is to get Guile
running inside Emacs so that I can have a nice workflow for exploring Hoot
at the REPL.
One way to do this is to use geiser-connect
: first, we start a terminal session with guix shell -m manifest.scm
in a directory with a manifest file consisting of
(use-modules (guix)
(guix packages)
(gnu packages base)
(gnu packages guile)
(gnu packages guile-xyz))
(packages->manifest (list guile-next guile-hoot gnu-make))
We can use an instance of Emacs that is already running. and then start a Guile REPL with guile --listen
. Next, we open a Scheme file in Emacs and enable geiser-mode
with M-x geiser-mode
. The final step is to connect Emacs to the Guile REPL
with M-x geiser-connect
and accept the defaults for host and port by typing enter at both prompts. Use localhost for host and 37146 for port, which is the default value.
Now we have a working Guile REPL inside Emacs with paredit
for paren matching, full-fledged support for the Guile REPL (e.g., M-p
enters the previous command at the REPL prompt), and the ability to evaluate code from the Scheme file with C-c C-e
, etc.
December 5
Goal: Figure out next steps for a rich web application using Hoot
.
Read through Dave Thompson’s tutorial again to get the gist of what needs to happen next. Now I have to learn more about Web Assembly, perhaps also by reviewing the specification.
December 4
Goal: Figure out a compilation workflow.
Got myself a nice little setup for the todo
application (just that) in Dave Thompson’s tutorial Building Interactive Web Pages with Guile Hoot: I added the littlest CSS to make things nicer, and have a nice edit code, make
, and make server
workflow for compiling Guile to Wasm and then deploying it locally. Now I can play with the code to my heart’s content.
Re-read Dave Thompson’s tutorial–lots of meat in there.
December 3
Goal: Figure out workflows.
The learning process needs to be supported by being able to execute code readily. Get examples running and recast tutorial into a file-based workflow.
First take: extract the TDD code from guile-hoot-ffi-demo
to create its own repository where I can study the make
code in the context of the information in the manual. As it turns out, this code base is extremely simple and relies solely on GNU Make
. This seems like a fine first step towards today’s goal.
December 2
Goal: Get the lay of the land–review the documentation to determine next steps.
Quick read of the manual for Hoot
. The documentation is good but terse, so I see the need for quite a bit of exploration. I also looked a little more into Web Assembly. The sum total of my explorations from today is that I should play with the REPL and with the tooling inside Guile.
The main motivation for trying to get my head around all the stuff is to be ready for Office Hours on December 11.
December 1
Goal: Install Hoot
and play with some of the sample code to get my footing.
Installing Hoot
to run the tutorial was trivial since I run Debian with Guix
as a package manager, except for the part where we want to run Wasm in the browser. For this we need reflect.js
, reflect.wasm
, and wtf8.wasm
.
Though I have been using Guix
for some time, after many years of having to deal with many systems, I have become intensely allergic to anything to do with DevOps and terminal shells, so it took me a while to realize that I could find the requisite files in the GNU store under guile-hoot
.
After a couple of false starts, including building NodeJS
on my laptop, I copied these files to the hoot-tutorial
directory, and completed the tutorial successfully.
My take on the tutorial: we can compile Guile code to a Wasm module. We can either instantiate the module to run it on an interpreter that is part of Hoot
‘s toolchain–not a small thing–or we can create a Wasm binary, write it to a file, and then use JavaScript and HTML to load and run the code in the browser.