# plzwrk A Haskell front-end framework. ## Hello world ```haskell import Web.Framework.Plzwrk import Web.Framework.Plzwrk.Asterius import Web.Framework.Plzwrk.Tag (p__) main :: IO () main = do browser <- asteriusBrowser plzwrk'_ (p__ "Hello world!") browser ``` See it [live](https://plzwrk-hello-world.surge.sh). ## Kitchen sink Check out the code [here](./kitchen-sink/Main.hs). See it [live](https://plzwrk-kitchen-sink.surge.sh). ## Installation Add `plzwrk` to the `build-depends` stanza of your `.cabal` file. Also, add `plzwrk-X.Y.Z.?` to the `extra-deps` list of your `stack.yaml` file if you're using stack. ## Making a webpage `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 `-f 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 root directory. Note the use of the `-f plzwrk-enable-asterius` flag in the `ahc-cabal` step. ```bash username@hostname:~/my-dir$ docker run --rm -it -v $(pwd):/project -w /project terrorjack/asterius asterius@hostname:/project$ ahc-cabal new-install -f plzwrk-enable-asterius --installdir asterius@hostname:/project$ cd && ahc-dist --input-exe --browser --bundle ``` ## Documentation The main documentation for `plzwrk` is on [hackage](https://hackage.haskell.org/package/plzwrk). The four importable modules are: - `Web.Frameworks.Plzwrk` for the basic functions - `Web.Frameworks.Plzwrk.Tag` for helper functions to make takes like `input` or `br`. - `Web.Frameworks.Plzwrk.MockJSVal` to use a mock browser. - `Web.Frameworks.Plzwrk.Asterius` to use a bindings for a real browser courtesy of [Asterius](https://github.com/tweag/asterius). ## Design `plzwrk` is inspired by [redux](https://redux.js.org/) for its state management. The main idea is that you have a HTML-creation function that accepts one or more variables from a state that is composed, via applicative functors, with getters from a state. ```haskell -- State data MyState = MkMyState { _name :: Text, age :: Int, _tags :: [Text] } -- Function hydrating a DOM with elementse from the state makeP = (\name age -> p'__ concat [name, " is the name and ", show age, " is my age."]) <$> _name <*> _age ``` HTML-creation functions can be nested, allowing for powerful abstractions. ```haskell nested = div_ (take 10 $ repeat makeP) ``` HTML-creation functions use an apostrophe after the tag name (ie `div'`) if they accept arguments and no apostrophe (ie `div`) if they don't. Additionally, tags that do not have any attributes (class, style etc) are marked with a trailing underscore (`div_ [p__ "hello"]`), and tags that only accept text are marked with two trailing underscores (`p__ "hello"`). The HTML-creation function itself should be pure with type `(s -> Node s opq)`, where `s` is the type of the state and `opq` is the type of the opaque pointer used to represent a JavaScript value. `opq` will rarely need to be provided manually as it is induced from the compiler based on the `Browserful` being used (ie `asteriusBrowser`). Event handlers take two arguments - an opaque pointer to the event and the current state - and return a new state (which could just be the original state) in the `IO` monad. For example, if the state is an integer, a valid event handler could be: ``` eh :: opq -> Int -> IO Int eh _ i = pure $ i + 1 ``` To handle events (ie extract values from input events, etc) you can use one of the functions exported by `Web.Framework.Plzwrk`. Please see the [hackage documentation](https://hackage.haskell.org/package/plzwrk) for more information. > If you are using the Asterius backend, callback functions are still quite fragile and subject to breakage. The less third-party libraries you use in them, the better. For example, avoid using `Data.Text` and `aeson` if possible. ## Testing your code Plzwrk comes with a mock browser that can act as a drop-in replacement for your browser. Use this in your tests. ```haskell import Web.Framework.Plzwrk.MockJSVal main :: IO () browser <- makeMockBrowser print "Now I'm using the mock browser." ``` ## Using Plzwrk should be considered experimental. It is unfit for production and the syntax will change frequently, often in non-backward-compatible ways. There is a [changelog](ChangeLog.md). ## Contributing Thanks for your interest in contributing! Here is a small list of things I'd really appreciate help with: - Static site rendering - Automatic code splitting - Hot reloading - Documentation