Extend an example

This article will show you how to build an example project, and how to load the development environment to suit your needs. First, however, you’ll need to fetch the source code:

git clone https://gitlab.com/platonic/shpadoinkle.git
cd Shpadoinkle

Building Examples

Shpadoinkle works with both GHC and GHCjs.

Building from source can take a long time. If you run with nix-build it’s highly recommended you use Cachix.

GHCjs

When built with GHCjs, Shpadoinkle Haskell code is compiled to JavaScript. This is, for the majority of use cases, the recommended path for running code in production.

nix-build --arg isJS true -A Shpadoinkle-examples

This will generate a folder called result. This contains the output of GHCjs, which you can serve to browsers. Here we use warp, which is a static file server written in Haskell. However, you can serve the output in any way you’d like.

nix-shell -p haskellPackages.wai-app-static --command "warp --docroot result/bin/counter.jsexe --port 8080"

Open localhost:8080 in your browser, and you should see the counter example.

GHC

When built with GHC, Shpadoinkle runs via JSaddle Warp. This allows frontend Haskell code to be compiled and executed as a backend binary. IO effects are sent to the browser over a websocket, where they are executed by a minimal client written in JavaScript.

nix-build --arg isJS false -A Shpadoinkle-examples

This will generate a folder called result. This contains the output of GHC. You can now serve an example.

./result/bin/counter

Open localhost:8080 in your browser, and you should see the counter example.

Live development

For live development, building a fully optimized build from scratch with nix-build can be too slow. When writing code, we need rapid feedback to be productive.

GHCID

Either "GHCi as a daemon" or "GHC + a bit of an IDE". To a first approximation, it opens GHCi and runs :reload whenever your source code changes, formatting the output to fit a fixed-height console.
— ndmitchell

GHCID is supplied out-of-the-box by the nix-shell in Shpadoinkle. You can use GHCID to get rapid feedback as follows:

nix-shell
ghcid --command "cabal repl examples:counter"

Or, as a one-liner:

nix-shell --command "ghcid --command 'cabal repl examples:counter'"

Repl Serving

The easiest way to get changes into your browser as fast as possible is to reload main in a REPL. There are two ways to do this — with cabal repl, or automatically with ghcid.

Manually

First, load the REPL:

nix-shell
cabal repl examples:counter

Then, run the application by executing main. This will serve the application on localhost:8080. When you make changes to the code, simply interrupt the process by pressing btn:[Ctrl + C], then reload and run main once again:

:reload
main

Refresh your browser to view updates.

Unfortunately, JSaddle’s websocket frontend code doesn’t yet support FireFox due to this issue. Work is underway to remediate this.

With GHCID

This will execute :reload and main every time the source code changes, because we’re using GHCID:

nix-shell --command "ghcid --command 'cabal repl examples:counter' --run"

Live reloading on the client is also possible:

nix-shell --command "ghcid --command 'cabal repl examples:counter' --run='Main.dev'"

Incremental Builds

Incremental builds also work just fine. To build the specific example, you can run nix-build for the examples attribute:

nix-build -A Shpadoinkle-examples

And likewise for GHCjs:

nix-build -A Shpadoinkle-examples --arg isJS true
If you run nix-build or nix-build --arg isJS true without specifying a specific attribute, there will be multiple result-<n> symlinked folders. However, if you do specify an attribute, then ./result/ (without a number suffix) will point to the most recent attribute built. However, if you’re ever confused as to which result points to where, just run ls -l. All symlinks point to an attribute name, so just look for Shpadoinkle-examples!

If you want to build only the counter example, though, that’s best done with a nix-shell:

nix-shell
cabal build examples:counter

Using cabal build directly will use the standard Haskell incremental builds. Results can be found in the ./dist-newstyle/ folder, instead of the result symlink elaborated on earlier.

Lastly, to build with GHCjs, just predicate the nix-shell with the associated argument:

nix-shell --arg isJS true
cabal build examples:counter