A tasty Haskell front-end framework
**Miso** is a small "[isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/)" [Haskell](https://www.haskell.org/) front-end framework featuring a virtual-dom, diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, Server-sent events, Websockets, type-safe [servant](https://haskell-servant.github.io/)-style routing and an extensible Subscription-based subsystem. Inspired by [Elm](http://elm-lang.org/), [Redux](http://redux.js.org/) and [Bobril](http://github.com/bobris/bobril). **Miso** is pure by default, but side effects (like `XHR`) can be introduced into the system via the `Effect` data type. **Miso** makes heavy use of the [GHCJS](https://github.com/ghcjs/ghcjs) FFI and therefore has minimal dependencies. ## Table of Contents - [Quick Start](#quick-start) - [Stack](#stack) - [Nix](#nix) - [Cabal](#cabal) - [GHCJSi Caveats](#ghcjsi-caveats) - [Architecture](#architecture) - [Examples](#examples) - [TodoMVC](#todomvc) - [Mario](#mario) - [Websocket](#websocket) - [SSE](#sse) - [XHR](#xhr) - [Router](#router) - [SVG](#svg) - [Simple](#simple) - [Haddocks](#haddocks) - [GHC](#ghc) - [GHCJS](#ghcjs) - [Sample Application](#sample-application) - [Building examples](#building-examples) - [Maintainers](#maintainers) - [Contributing](#contributing) - [License](#license) ## Quick start To get started quickly building applications, we recommend using the [`stack`](https://docs.haskellstack.org/en/stable/README/) or [`nix`](https://nixos.org/nix) package managers. Obtaining [`GHCJS`](https://github.com/ghcjs/ghcjs) is required as a prerequisite. `stack` and `nix` make this process easy, if you're using `cabal` we assume you have [obtained `GHCJS`](https://github.com/ghcjs/ghcjs#installation) by other means. All source code depicted below for the quick start app is available [here](https://github.com/dmjio/miso/tree/master/sample-app). ### Stack In the `miso` repository there is a [folder named `stack`](https://github.com/dmjio/miso/tree/master/stack) with "known to work" configurations for `GHCJS`. One stack file exists for both the `7.10.3` and `8.0.1` versions of `GHCJS`. In general, we recommend developing with the `7.10.3` version since it currently supports `GHCJSi` (a REPL that connects to the browser by way of a [`nodejs`](https://nodejs.org/en/) web server using [`socket.io`](https://socket.io/)) and building with the `8.0.1` version (if possible). For more information on using `stack` with `GHCJS`, please consult the [GHCJS section of the `stack` docs](https://docs.haskellstack.org/en/stable/ghcjs/). To begin, create the following directory layout ```bash ➜ mkdir app && touch app/{Main.hs,app.cabal,stack.yaml} && tree app app |-- Main.hs |-- app.cabal `-- stack.yaml ``` Add a `stack.yaml` file that uses a recent version of `miso`. ```bash ➜ cat app/stack.yaml resolver: lts-6.20 compiler: ghcjs-0.2.0.9006020_ghc-7.10.3 compiler-check: match-exact packages: - '.' extra-deps: - miso-0.4.0.0 setup-info: ghcjs: source: ghcjs-0.2.0.9006020_ghc-7.10.3: url: http://ghcjs.tolysz.org/lts-6.20-9006020.tar.gz sha1: a6cea90cd8121eee3afb201183c6e9bd6bacd94a ``` Add a `cabal` file ```bash ➜ cat app/*.cabal name: app version: 0.1.0.0 synopsis: First miso app category: Web build-type: Simple cabal-version: >=1.10 executable app main-is: Main.hs build-depends: base, miso default-language: Haskell2010 ``` Add the source from [Sample Application](#sample-application) to `app/Main.hs` Run `stack setup`. This might take a long time, since it will have to build `GHCJS`. ``` stack setup ``` Run `stack build` to get the static assets ``` stack build ``` See the result ``` open $(stack path --local-install-root)/bin/app.jsexe/index.html ``` Using GHCJSi ``` stack ghci ``` If that warns with `socket.io not found, browser session not available`, you'll need to install `socket.io` ``` npm install socket.io ``` and update your `NODE_PATH` ``` export NODE_PATH=$(pwd)/node_modules ``` Now you should be connected, and the app viewable in `GHCJSi` (open http://localhost:6400). ```bash ➜ stack ghci app-0.1.0.0: initial-build-steps (exe) Configuring GHCi with the following packages: app GHCJSi, version 0.2.0.9006020-7.10.3: http://www.github.com/ghcjs/ghcjs/ :? for help [1 of 1] Compiling Main ( /Users/david/Desktop/miso/sample-app/Main.hs, interpreted ) socket.io found, browser session available at http://localhost:6400 Ok, modules loaded: Main. *Main> main browser connected, code runs in browser from now on ``` ### Nix `Nix` is a more powerful option for building web applications with `miso` since it encompasses development workflow, configuration management, and deployment. The source code for [`haskell-miso.org`](https://github.com/dmjio/miso/tree/master/examples/haskell-miso.org) is an example of this. If unfamiliar with `nix`, we recommend [@Gabriel439](https://github.com/Gabriel439)'s ["Nix and Haskell in production"](https://github.com/Gabriel439/haskell-nix) guide. To get started, we will use the [`cabal2nix`](https://github.com/NixOS/cabal2nix) tool to convert our `Cabal` file into a `nix` derivation (named `app.nix`). We'll then write a file named `default.nix`, which is used for building our project (via `nix-build`) and development (via `nix-shell`). To begin, make the following directory layout: ```bash ➜ mkdir app && touch app/{Main.hs,app.cabal,default.nix,app.nix} && tree app app |-- Main.hs |-- app.cabal |-- default.nix `-- app.nix ``` Add a `cabal` file ```bash ➜ cat app/*.cabal name: app version: 0.1.0.0 synopsis: First miso app category: Web build-type: Simple cabal-version: >=1.10 executable app main-is: Main.hs build-depends: base, miso default-language: Haskell2010 ``` Use [`cabal2nix`](https://github.com/NixOS/cabal2nix) to generate a file named `app.nix` that looks like below. ```bash ➜ cabal2nix . --compiler ghcjs > app.nix ➜ cat app.nix ``` ```nix { mkDerivation, base, miso, stdenv }: mkDerivation { pname = "app"; version = "0.1.0.0"; src = ./.; isLibrary = false; isExecutable = true; executableHaskellDepends = [ base miso ]; description = "First miso app"; license = stdenv.lib.licenses.unfree; } ``` Write a `default.nix` (which calls `app.nix`), this fetches a recent version of `miso`. ```nix { pkgs ? import