Reflex Tutorial Upgrade (Part 5)
I first attempted to upgrade the Reflex FRP calculator tutorial to use the latest version of Obelisk on the first, and I had no idea that I would still be pulling on that thread ten days later. My goal was to get some practice with managing a Reflex FRP project using Nix. I was indeed able to do that, but it is unfortunate that I gave up on that task without success. This blog entry is for reflections.
This task has taken a lot more time than I had anticipated. The primary reason is that it has not been my priority project. I have been busy with other things, including reading Really Feely: Farm and computer science papers to my baby. Long Nix build times have definitely contributed to the slow progress as well, though.
I gave up on trying to get the project to build using
ob run
when I reached ghcid issues with the
Markdown-formatted source code. This is not an issue that I want to work
on, as I do not think I will ever have such a situation in an actual
project.
I stopped trying to get the project to build using
nix-build
just because I do not want to allocate any more
time to this. The project uses a fork of mmark with commits
that are not merged into newer versions. That fork does not work with
the newer versions of dependencies that are provided by the new version
of Obelisk. Creating a fork from a newer version of mmark
and porting the special commits would likely resolve that issue.
Nix Cache
One thing that I was able to confirm is that my Nix cache settings
are working. I was using the single-user mode when I started, and I was
unsure if the cache was working fully because build times were so slow.
I was unable to find any definitive documentation that states that
/etc/nix/nix.conf
is used with single-user mode.
I figured out how to do a multi-user Nix installation on my system. The permissions issue that I ran into before (see Uninstalling Multi-User Nix) seems to have been fixed, and I was able to debug the mess of shell configuration. I was able to see how things work without cache before restarting the Nix daemon, as well as confirm that cache was being used afterwards. I confirmed that the cache settings were indeed being used in the single-user mode. Slow build times are normal, and my system is not very fast.
ob --verbose
Issue
Using the latest release of Obelisk, I ran into the ob --verbose
invalid character failure issue, which made it impossible for me to
see actual build failures. I initially got around this issue by hacking
Obelisk and replacing Unicode characters with ASCII characters. A friend
relayed some information about the cause, which I wrote about in the Nix Terminfo and
Local Archive blog entry. I implemented a hack that sets locale
environment variables to C.UTF-8
, re-executing the program
when necessary.
nix-thunk
Hacking on Obelisk gave me a chance to really try out the various
ob thunk
commands, implemented using nix-thunk.
From a developer perspective, it provides a really convenient
way to develop changes to dependencies. I really enjoyed using it!
Note that I was first introduced to nix-thunk by a friend last summer, when I was trying to use Nix to test software across many different versions of GHC. I ended up not using it because it is overkill for the simple task of selecting nixpkgs revisions that I would never unpack. It is much more useful in a Reflex FRP project!
Maintenance
I have the impression that Nix generally makes project maintenance difficult/expensive, and this experience did not change that impression. Nix projects tend to be carefully constructed snapshots of versions of software packages that work together. Upgrading seems to be a major task, because a new snapshot of mutually-compatible package versions needs to be found or created. I can easily imagine such projects getting stuck on old versions of dependencies and being very difficult to update, especially if there is employee turnover and the original developers are no longer around.
The mmark
dependency illustrates one type of maintenance
problem. A fork (branch) was created in order to make some changes that
were required for the project, but those changes were never merged
upstream. Such a dependency on a special branch increases the difficulty
of upgrading the project to use newer dependencies because that
dependency cannot simply be upgraded. I have seen many branches
in repositories related to Reflex FRP and had assumed that they are
feature branches, but I now worry that there may be a lot of such forks.
“Feather branching” can make software difficult to maintain indeed.
The functionality that Reflex FRP provides is amazing, and Nix is a very important part of that. From a project management perspective, what can be done to make projects easier to maintain? The following are some ideas to try out in my own Reflex FRP projects.
One thing that I think would help is having clear releases. For
example, I like to make/merge commits into a develop
branch
and only commit releases to the main
/master
branch. With a clear separation between development and releases,
different quality standards can be established to greatly improve the
quality of releases without putting too much burden on development.
Hacking on dependencies using nix-thunk is really convenient, but I think that some discipline is required to make things easier to maintain. For example, perhaps it would be a good policy to document any thunk dependencies that are not themselves releases. If a thunk points to a fork, then the parent project should document why that fork is being used instead of a release. It would probably also be wise to create releases that only use releases of dependencies, not forks. Forks are fine during development, but changes should be released before they are used in the release of another project.