Skip to content

dmjio/miso

miso

AtastyHaskellfront-end framework

Miso Slack Haskell Cachix GitHub Actions Hackage IRC #haskell-miso LICENSE

Misois a small, production-ready, "isomorphic"Haskellfront-end framework for quickly building highly interactive single-page web applications. It features a virtual-dom, recursive diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, Server-sent events, Websockets, type-safeservant-style routing and an extensible Subscription-based subsystem. Inspired byElm,ReduxandBobril.Misois pure by default, but side effects (likeXHR) can be introduced into the system via theEffectdata type.Misomakes heavy use of theGHCJSFFI and therefore has minimal dependencies.Misocan be considered a shallowembedded domain-specific languagefor modern web programming.

Table of Contents

Quick start

To get started quickly building applications, we recommend using thenixpackage manager with miso's binary cache provided bycachix.It is possible to usestackto build GHCJS projects, but support for procuringGHCJShas been removedas of stack 2.0.nixis used to procure a working version ofGHCJS.If you're usingcabalwe assume you haveobtainedGHCJSby other means. All source code depicted below for the quick start app is availablehere.

Begin

To build the sample-app withnix,execute the commands below:

#optional use of cache
nix-env -iA cachix -f https://cachix.org/api/v1/install
#optional use of cache
cachix use miso-haskell
git clone https://github.com/dmjio/miso
cdmiso/sample-app
nix-build
open./result/bin/app.jsexe/index.html

The above commands will add miso's binary cache to your nix installation (support for both Linux and OSX). nix-buildwill fetch the dependencies from miso's cache and build the sample application.

Nix

Nixis a more powerful option for building web applications withmisosince it encompasses development workflow, configuration management, and deployment. The source code forhaskell-miso.orgis an example of this.

If unfamiliar withnix,we recommend@Gabriella439's"Nix and Haskell in production"guide.

To begin, make the following directory layout:

➜ mkdir app&&touch app/{Main.hs,app.cabal,default.nix}&&tree app
app
|-- Main.hs
|-- app.cabal
`-- default.nix

Add acabalfile

➜ 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
ghcjs-options:
-dedupe
build-depends: base, miso
default-language: Haskell2010

Write adefault.nix(this will fetch a recent version ofmiso).misowill provide you with a workingnixpkgsnamedpkgs.callCabal2nixwill automatically produce a nix expression that builds your cabal file.

with(import(builtins.fetchGit{
url="https://github.com/dmjio/miso";
ref="refs/tags/1.8";
}){});
pkgs.haskell.packages.ghcjs.callCabal2nix"app"./.{}

Add the source fromSample Applicationtoapp/Main.hs

Build the project

nix-build

Open the result

open./result/bin/app.jsexe/index.html

For development withnix,it can be nice to havecabalpresent for building. This command will make it available in yourPATH.

nix-env -iA cabal-install -f '<nixpkgs>'

To be put into a shell w/GHCJSand all the dependencies for this project present, usenix-shell.

nix-shell -A env

To view the dependencies for your project, callghcjs-pkg listwhen inside the shell.

nix-shell -A env --run 'ghcjs-pkg list'

To build the project withcabalafter entering thenix-shell

nix-shell -A env --run 'cabal configure --ghcjs && cabal build'

For incremental development inside of thenix-shellwe recommend using a tool likeentrto automatically rebuild on file changes, or roll your own solution withinotify.

ag -l | entr sh -c 'cabal build'

Architecture

For constructing client and server applications, we recommend using onecabalfile with two executable sections, where thebuildableattribute set is contingent on the compiler. An example of this layout ishere.For more info on how to usestackwith aclient/serversetup, see thislink.For more information on how to usenixwith aclient/serversetup, see thenix scriptsforhttps://haskell-miso.org.

Examples

TodoMVC

Flatris

2048

Snake

Mario

Miso Plane (Flappy Birds)

Websocket

SSE

XHR

Router

SVG

Canvas 2D

ThreeJS

Simple

File Reader

WebVR

Pixel Card Wars

Currency Converter

Haddocks

GHCJS

GHC

Sample application

--|Haskell language pragma
{-#LANGUAGEOverloadedStrings #-}
{-#LANGUAGERecordWildCards #-}

--|Haskell module declaration
moduleMainwhere

--|Miso framework import
importMiso
importMiso.String

--|Type synonym for an application model
typeModel=Int

--|Sum type for application events
dataAction
=AddOne
|SubtractOne
|NoOp
|SayHelloWorld
deriving(Show,Eq)

--|Entry point for a miso application
main::IO()
main=startAppApp{..}
where
initialAction=SayHelloWorld--initial action to be executed on application load
model=0--initial model
update=updateModel--update function
view=viewModel--view function
events=defaultEvents--default delegated events
subs=[]--empty subscription list
mountPoint=Nothing--mount point for application (Nothing defaults to 'body')
logLevel=Off--used during prerendering to see if the VDOM and DOM are in sync (only applies to `miso` function)

--|Updates model, optionally introduces side effects
updateModel::Action->Model->EffectActionModel
updateModel action m=
caseactionof
AddOne
->noEff (m+1)
SubtractOne
->noEff (m-1)
NoOp
->noEff m
SayHelloWorld
->m<#doconsoleLog"Hello World">>pureNoOp

--|Constructs a virtual DOM from a model
viewModel::Model->ViewAction
viewModel x=div_[][
button_ [ onClickAddOne] [ text"+"]
,text (ms x)
,button_ [ onClickSubtractOne] [ text"-"]
]

Transition application

An alternative, more powerful interface for constructingmisoapplications is using theTransitioninterface. Transitionis based on theStateTmonad transformer, and can be used to construct components. It also works very nicely with lenses based onMonadState(i.e.(.=),(%=),(+=),(-=)).

--|Haskell language pragma
{-#LANGUAGEOverloadedStrings #-}
{-#LANGUAGERecordWildCards #-}

--|Haskell module declaration
moduleMainwhere

--|Miso framework import
importMiso
importMiso.String

--|Lens import
importControl.Lens

--|Type synonym for an application model
dataModel
=Model
{_counter::Int
}deriving(Show,Eq)

counter::Lens'ModelInt
counter=lens _counter$\record field->record { _counter=field }

--|Sum type for application events
dataAction
=AddOne
|SubtractOne
|NoOp
|SayHelloWorld
deriving(Show,Eq)

--|Entry point for a miso application
main::IO()
main=startAppApp{..}
where
initialAction=SayHelloWorld--initial action to be executed on application load
model=Model0--initial model
update=fromTransition.updateModel--update function
view=viewModel--view function
events=defaultEvents--default delegated events
subs=[]--empty subscription list
mountPoint=Nothing--mount point for application (Nothing defaults to 'body')
logLevel=Off--used during prerendering to see if the VDOM and DOM are in sync (only applies to `miso` function)

--|Updates model, optionally introduces side effects
updateModel::Action->TransitionActionModel()
updateModel action=
caseactionof
AddOne
->counter+=1
SubtractOne
->counter-=1
NoOp
->pure()
SayHelloWorld
->scheduleIO_ (consoleLog"Hello World")

--|Constructs a virtual DOM from a model
viewModel::Model->ViewAction
viewModel x=div_[][
button_ [ onClickAddOne] [ text"+"]
,text.ms$x^.counter
,button_ [ onClickSubtractOne] [ text"-"]
]

Live reload with JSaddle

It is possible to buildmisoapplications withghcid,jsaddlethat allow live reloading of your application in reponse to changes in application code. See theREADMEin thesample-app-jsaddlefolder for more information.

Docker

Developing miso applications inside a Docker container is supported (allows applications to be built on Windows). See theREADMEin thedockerfolder for more information.

Building examples

The easiest way to build the examples is with thenixpackage manager

git clone https://github.com/dmjio/miso && cd miso && nix-build --arg examples true

This will build all examples and documentation into a folder namedresult

➜ miso git:(master) ✗ tree -d./result/bin
./result/bin
|-- canvas2d.jsexe
|-- compose-update.jsexe
|-- file-reader.jsexe
|-- mario.jsexe
| `-- imgs
|-- mathml.jsexe
|-- router.jsexe
|-- simple.jsexe
|-- svg.jsexe
|-- tests.jsexe
|-- threejs.jsexe
|-- todo-mvc.jsexe
|-- websocket.jsexe
`-- xhr.jsexe

To see examples, we recommend hosting them with a webserver

cd result/bin/todo-mvc.jsexe && nix-shell -p python --run 'python -m SimpleHTTPServer'
Serving HTTP on 0.0.0.0 port 8000...

Coverage

The core algorithmic component of miso isdiff.js.It is responsible for all DOM manipulation that occurs in a miso application and has100% code coverage.Tests and coverage made possible usingjsdomandjest.

To run the tests and build the coverage report:

cdmiso/tests
npm i
npm runtest
## Or by using `yarn` instead of `npm`:
#yarn
#yarn test

Isomorphic

Isomorphic javascriptis a technique for increased SEO, code-sharing and perceived page load times. It works in two parts. First, the server sends a pre-rendered HTML body to the client's browser. Second, after the client javascript application loads, the pointers of the pre-rendered DOM are copied into the virtual DOM, and the application proceeds as normal. All subsequent page navigation is handled locally by the client, avoiding full-page postbacks as necessary.

Themisofunction is used to perform the pointer-copying behavior client-side.

For more information on howmisohandles isomorphic javascript, we recommendthis tutorial.

Pinning nixpkgs

By defaultmisouses a known-to-work, pinned version ofnixpkgs.

Binary cache

nixusers on a Linux or OSX distro can take advantage of abinary cachefor faster builds. To use the binary cache follow the instructions oncachix.

cachix use miso-haskell

Benchmarks

According to benchmarks,misois among the fastest functional programming web frameworks, second only toElm.

Maintainers

@dmjio

Commercial Users

Contributing

Feel free to dive in!Open an issueor submitPRs.

SeeCONTRIBUTINGfor more info.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

BSD3© David Johnson