by Marc Scholten, 10.08.2020
IHP is using Haskell for building your application code. Haskell code typically needs to be compiled before you can run your application code. IHP provides a built-in development server that provides instant feedback when making changes to your application code. In this post we're going to explain the technical details behind the magic.
Let's first take a look at what happens when you start your IHP app by running ./start
.
The ./start
script in your project is basically just a small wrapper that makes sure that all dependencies that are managed by nix are available and then calls the RunDevServer
binary. This binary is part of IHP and does the actual application startup as well as managing the postgres server.
You can think of RunDevServer
as a small process manager. When RunDevServer
is started, it will directly start all processes required for your application to run:
All these processes are started in parallel for fast performance and synchronised in the main event loop of the dev server. Made possible thanks to haskells great concurrency capabilities.
The most important process is the ghci process with your app. Instead of fully recompiling your app on every file change, IHP loads your app in the repl and then refreshes only the changed files.
At first ghci needs a couple of seconds to load all haskell files of your application. While loading your application the status server is serving all http requests on localhost:8000. The status server will also show all type errors in case ghci failed to load your app. When the ghci finished loading, the status server is stopped and your application is started on localhost:8000.
Once your application is started, the dev server mainly deals with file changes. Using a file watcher the dev server is notified about any changes to your haskell files in your project. When a haskell file is changed the app process running in the ghci process is stopped and a refresh (:r
) is triggered. The refresh is usually very fast. Once completed the app server will be started again.
Once the haskell app has started, open browser pages will be notified using a websocket connection. The open pages will fetch the current page via ajax and will then update the dom using a diff and patch approach. So only dom nodes that have actually changed will be touched during the live reloading. This approach will keep existing page state like scroll position or text typed into a form field. It allows for a very fast and productive feedback cycle.
Did you know: #ihp also supports live reloading of CSS. pic.twitter.com/uRCCr3gkHz
— digitally induced (@digitallyinduce) June 29, 2020
IHP also supports live reloading of CSS files. Once IHP sees a file change to your CSS files in the static
directory it will notify all open browser tabs using its websocket connection. Once notified in the browser IHP will look for any <link rel="stylesheet">
and will reload the css file using a cache buster.
This is only possible because nix allows us to pin down the set of package definitions to a specific git commit of the nix package registry.
Sometimes you make a change which will stop your application from compiling. In these error cases the status server jumps in and starts listening on localhost:8000. The status server will then display the error message so you can quickly fix it:
Additionally open browser tabs will be notified about this and will refresh. This way the error is instantly visible to you.
While the above steps are technically complicated, when doing actual development you will not see much of this complexity. Lots of time have been spent to find the best approach and smoothing out all the edge cases. The whole process is very much inspired by PHP where you just make file changes and it works. The live reloading really just works
.
The dev server is the heart of IHP and makes the dev process extremely productive. You get all the benefits of type-safety with the development speed you previously only got with scripting languages. If you haven't already it's time to try it out! 🚀
Feel free to share this post on Twitter, Reddit, Hacker News or anywhere else on the internet :)
To get notified about the latest updates, subscribe to the IHP newsletter.
You can also follow digitally induced on Twitter.
Leave a star at the GitHub repo: digitallyinduced/ihp
Questions, or need help with haskell type errors? Join us at Gitter (IRC Bridge available)