Skip to main content

Reflex Tutorial Upgrade (Part 4)

In my fourth attempt to update the Reflex FRP calculator tutorial to use the latest release of Obelisk, I was finally able to see the actual error that was causing the build failure. Perhaps now I can resolve it!

The build has been failing because mmark 0.0.7.1 requires megaparsec >=7.0 && <8.0, but that constraint cannot be resolved. First, I would like to confirm the versions of these packages.

I am using Obelisk revision e3ec75f698. The dep/reflex-platform directory specifies Reflex Platform revision 123a6f487c. The nixpkgs directory specifies obsidiansystems/nixpkgs revision a78062dc78. The hackage-packages.nix files specifies that versions mmark 0.0.7.2 and megaparsec 8.0.0. These versions are consistent, but that is not the version of mmark in the error message, so some configuration must specify a different version.

I found the configuration in the dep/mmark directory of the calculator-tutorial repository. It specifies obsidiansystems/mmark revision 350aee0f2d, which is indeed mmark 0.0.7.1. When upgrading Obelisk, newer versions of Reflex Platform and nixpkgs are used, bringing along new versions of dependencies, and this version of mmark does not work with the newer version of megaparsec.

This special version of mmark must have been used for a reason. It is on a branch named th-lift+expose-internal, which may give a clue, but I do not see any further documentation.

Perhaps the newer version of mmark in the new nixpkgs includes the changes that were required for the tutorial? This is easy to test by commenting out the hackGet line for mmark in the default.nix file.

diff --git a/default.nix b/default.nix
index d12d53b..2091741 100644
--- a/default.nix
+++ b/default.nix
@@ -16,7 +16,7 @@ with pkgs.haskell.lib; {
   ios.bundleName = "Obelisk Minimal Example";
   __closureCompilerOptimizationLevel = null;
   packages = {
-    mmark = hackGet ./dep/mmark;
+    # mmark = hackGet ./dep/mmark;
     modern-uri = hackGet ./dep/modern-uri;
   };
   overrides = self: super: {

I tried building, and it failed with the following error.

frontend/src/Tutorial.lhs:1:1: error:
    File name does not match module name:
    Saw: ‘Main’
    Expected: ‘Tutorial’
  |
1 | # Tutorial
  | ^

That .lhs file is not in the Literate Haskell format that is supported by GHC. It is a Markdown file with the Haskell code in fenced code blocks, not prefixed with “bird tracks.” This is done so that the Markdown can be displayed on GitHub. The markdown-unlit program is used to convert from Markdown to valid Haskell source code.

Note that this is the opposite of what LiterateX does. When using markdown-unlit, the source code is generated from a Markdown file. When using LiterateX, the Markdown documentation is generated from the source code.

The markdown-unlit utility does not use mmark as a dependency, so I do not know what is going wrong. The use of markdown-unlit is configured in the frontend.cabal file as follows.

ghc-options: -Wall -pgmL markdown-unlit

The markdown-unlit utility is added to the frontend build tools in the default.nix file as follows.

frontend = overrideCabal super.frontend (drv: {
  buildTools = (drv.buildTools or []) ++ [ self.buildHaskellPackages.markdown-unlit ];
});

The above error is displayed in an annoying full-screen interface that hides any previous output. I was unable to determine a way to exit the interface except for Ctrl+C. After exiting, I was able to scroll up and see that the output before the error has to do with running GHCi! The command to load GHCi added markdown-unlit as a visible package, but it does not include -pgmL markdown-unlit.

I found a file named ghcid-output.txt that contains the above error! Perhaps ghcid is failing to load the source from the Markdown file. I do not want to give up, but this is not a type of problem that I will run into when working on other projects. I doubt that ghcid is used for deployment, so I tried deploying locally to see if the project builds.

$ nix-build -A exe --no-out-link

This attempt to build failed with the following error. The newer version of mmark does not include the changes that were added to the fork, unfortunately. The tutorial relies on these special changes.

Building library for frontend-0.1..
[1 of 4] Compiling Reflex.MMark.Render ( src/Reflex/MMark/Render.hs, dist/build/Reflex/MMark/Render.js_o )

src/Reflex/MMark/Render.hs:32:1: error:
    Could not find module ‘Text.MMark.Internal.Type’
    Use -v to see a list of the files searched for.
   |
32 | import Text.MMark.Internal.Type hiding (Render (..))
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/Reflex/MMark/Render.hs:33:1: error:
    Could not find module ‘Text.MMark.Internal.Util’
    Use -v to see a list of the files searched for.
   |
33 | import Text.MMark.Internal.Util
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: builder for '/nix/store/818amz2yy1a9nn3iv9y7zqp4b9j3br6l-frontend-0.1.drv' failed with exit code 1;
       last 10 log lines:
       > 32 | import Text.MMark.Internal.Type hiding (Render (..))
       >    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       >
       > src/Reflex/MMark/Render.hs:33:1: error:
       >     Could not find module ‘Text.MMark.Internal.Util’
       >     Use -v to see a list of the files searched for.
       >    |
       > 33 | import Text.MMark.Internal.Util
       >    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       >
       For full logs, run 'nix log /nix/store/818amz2yy1a9nn3iv9y7zqp4b9j3br6l-frontend-0.1.drv'.

The changes in the mmark fork are pretty small and simple, so I could try applying the changes to a newer version of mmark that is compatible with the package versions available in the latest version of Obelisk. I think I am going to stop here, though, as I do not want to allocate any more time to this. I have learned a lot, though, and I will likely write one more blog entry with reflections.