Introducing plzwrk - A Haskell front-end framework
Tags: haskell, webdev, frontend
Published: 2020-05-14 14:58:38

---
title: Introducing plzwrk - A Haskell front-end framework
description: Build type-safe web apps using plzwrk, Asterius, and Haskell.
slug: introducing-plzwrk
published: true
canonical_url: https://meeshkan.com/blog/plzwrk
authors: ["mike"]
tags:
  - haskell
  - webdev
  - frontend
---

Table of Contents

- [A hello world in plzwrk](#a-hello-world-in-plzwrk)
- [Composing elements](#composing-elements)
- [Stateful elements](#stateful-elements)
- [Getting started with plzwrk](#getting-started-with-plzwrk)
- [Thanks](#thanks)

I really like programming in Haskell. Recently, I learned about [Asterius](https://github.com/tweag/asterius), a Haskell-to-WebAssembly compiler. So I thought it'd be fun to create a small frontend library to build websites that are compiled with Asterius.

Voilà [`plzwrk`](https://github.com/meeshkan/plzwrk). 

I've used it to build a couple [toy websites](https://github.com/meeshkan/plzwrk) and I must say the process has been quite delightful. In this article, I'd like to present a brief overview of `plzwrk`. If you're a frontend developer and interested in Haskell, I hope that you'll give it a try!

## A hello world in plzwrk

Here it is!

```haskell
{-# LANGUAGE QuasiQuotes       #-}

import           Web.Framework.Plzwrk
import           Web.Framework.Plzwrk.Asterius

main :: IO ()
main = do
  browser <- asteriusBrowser
  let element = [hsx|<p>Hello world!</p>|]
  plzwrk'_ element browser
```

Let's unpack what's going on.

On the first line of the `main` function, we are creating a browser.  `plzwrk` ships with two browser representations - one backed by [Asterius](https://github.com/meeshkan/plzwrk/blob/6ccee37d5190873d455b4d648d78dfda9ad59edc/src/Web/Framework/Plzwrk/Asterius.hs#L34) (which we use above) - and a [mock](https://github.com/meeshkan/plzwrk/blob/6ccee37d5190873d455b4d648d78dfda9ad59edc/src/Web/Framework/Plzwrk/MockJSVal.hs#L648) one that is useful for testing.

On the second line of `main`, we create our element to be inserted into the DOM. `plzwrk` has two ways to insert elements into the DOM:

- `hsx`, which is similar to [`jsx`](https://reactjs.org/docs/introducing-jsx.html).
- Functions like `div` and `img` from [`Web.Framework.Plzwrk.Tag`](https://github.com/meeshkan/plzwrk/blob/master/src/Web/Framework/Plzwrk/Tag.hs) that correspond to HTML tags (ie `div` creates a `<div></div>` tag).

If you've used `jsx` or templating languages like [Nunjucks](https://mozilla.github.io/nunjucks/) before, `hsx` will probably be more comfortable.

The third and last line sends the element and the browser to `plzwrk` for rendering. [Here's how it looks](https://plzwrk-hello-world.surge.sh).

## Composing elements

Here's how we can add an element into another element in `plzwrk`:

```haskell
main :: IO ()
main = do
  browser <- asteriusBrowser
  let element = [hsx|<p>Hello world!</p>|]
  let bigger = [hsx|<div style="background-color:red">
    #el{replicate 10 element}#
  </div>
  |]
  plzwrk'_ bigger browser
```

This takes our "Hello world!" and repeats it 10 times.

`#el{}#` tells `hsx` to expect a list of elements between the curly brackets. `hsx` currently has four types of Haskell values that it can accept in curly brackets:

- `el` is a list of elements.
- `e` is a single element.
- `t` is a single piece of text, either in the body of an element or as an attribute.
- `c` is a callback that one would supply to `click` or `input`.

Let's use them all in the example below:

```haskell
main :: IO ()
main = do
  browser <- asteriusBrowser
  let who = "world"
  let mystyle = "background-color:red"
  let element = [hsx|<p>Hello #t{who}#</p>|]
  let bigger = [hsx|<div click=#c{(\e s -> (consoleLogS browser) "clicked")}# style=#t{mystyle}#>
    #el{replicate 10 element}#
    #e{element}#
  </div>
  |]
  plzwrk'_ bigger browser
```

In the lambda function `#c{(\e s -> return $ (consoleLogS browser) "clicked")}#`, we see two arguments passed to the listener: `e` is an event and `s` is the state.

Speaking of state, let's see how `plzwrk` handles state!

## Stateful elements

All elements that accept arguments from a state are created with `hsx'` (note the apostrophe) or with a function like `div'` or `img'` (again, note the apostrophe).

```haskell
let elt = (\name -> [hsx'|<p>#t{name}#</p>|])
```

When it's time to render using `plzwrk`, we compose `elt` with a getter from a state that will hydrate name:

```haskell
newtype Person = Person { _name :: String }

main :: IO ()
main = do
  browser <- asteriusBrowser
  let elt = (\name -> [hsx'|<p>#t{name}#</p>|])
  plzwrk' (elt <$> _name) (Person "Stacey") browser
```

You can see the above example live [here](https://plzwrk-hello-world-state.surge.sh).

All event listeners in `plzwrk` return a modified state, and this modified state is then used to update the DOM.

```haskell

newtype Person = Person { _name :: String }

main :: IO ()
main = do
  browser <- asteriusBrowser
  let elt = (\name -> [hsx'|<div>
    <p>#t{name}#</p>
    <button click=#c{(\_ s -> return $ Person "Bob")}#>Change name</button>
  </div>|])
  plzwrk' (elt <$> _name) (Person "Stacey") browser
```

When we click the "Change name" button, the name will change from "Stacey" to "Bob" in the UI.

The pattern above allows elements to declaritively state their dependencies and leave it to the implementation of the state to provide these dependencies. For example, `elt` accepts any string as a name, and it's only when we call `elt <$> _name` that we link it to the `Person` type.

```haskell

data Person = Person { _name :: String, _age :: Int }

main :: IO ()
main = do
  browser <- asteriusBrowser
  let mystyle = "background-color:pink"
  let element = (\age -> [hsx'|<p>You just turned <span>#t{show age}#</span>. Congrats!</p>|]) <$> _age
  let bigger = (\name age -> [hsx'|<div style=#t{mystyle}#>
    <h1>#t{name}#</h1>
    #el{replicate age element}#
    #e{p__ ":-)"}#
  </div>
  |]) <$> _name <*> _age
  plzwrk' bigger (Person "Joe" 42) browser
```
You can see the above example live [here](https://plzwrk-hello-world-repeat.surge.sh).

In the above example, the phrase "You just turned 42. Congrats!" will be printed 42 times.

Both `element` and `bigger` are composed with getters from a state. This is similar to the strategy used in [Redux](https://redux.js.org/) with one caveat - instead of passing setters to elements, we pass the entire state to event handlers. This keeps the actual elements pure (i.e. no accidentally making a network call from the DOM construction function) and allows for maximum flexibility in working with the state.

## Getting started with plzwrk

`plzwrk` uses [Asterius](https://github.com/tweag/asterius) as its backend for web development. Compiling an application using `plzwrk` is no different than compiling an application using `ahc-cabal` and `ahc-dist` as described in the [Asterius documentation](https://asterius.netlify.app) with **one caveat**. You **must** use `--constraint "plzwrk +plzwrk-enable-asterius"` when running `ahc-cabal`.

A minimal flow is shown below, mostly copied from the Asterius documentation. It assumes that you have a cabal-buildable project in the `pwd`. Note the use of the `--constraint "plzwrk +plzwrk-enable-asterius"` flag in the `ahc-cabal` step:

```bash
username@hostname:~/my-dir$ docker run --rm -it -v $(pwd):/project -w /project meeshkan/plzwrk
asterius@hostname:/project$ ahc-link --input-hs Main.hs --browser --bundle
```

## Thanks

Thanks for checking out `plzwrk`! I'm looking forward to seeing what people build with it. If you've built something with `plzwrk` and would like to show it off in the README, if you would like to see any features or if you spot a bug, please [file an issue](https://github.com/meeshkan/plzwrk/issues) on GitHub!