Git Product home page Git Product logo

miso's Introduction

miso

A tasty Haskell front-end framework

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

Miso is a small, production-ready, "isomorphic" Haskell front-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-safe servant-style routing and an extensible Subscription-based subsystem. Inspired by Elm, Redux and 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 FFI and therefore has minimal dependencies. Miso can be considered a shallow embedded domain-specific language for modern web programming.

Table of Contents

Quick start

To get started quickly building applications, we recommend using the nix package manager with miso's binary cache provided by cachix. It is possible to use stack to build GHCJS projects, but support for procuring GHCJS has been removed as of stack 2.0. nix is used to procure a working version of GHCJS. If you're using cabal we assume you have obtained GHCJS by other means. All source code depicted below for the quick start app is available here.

Begin

To build the sample-app with nix, 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
cd miso/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-build will fetch the dependencies from miso's cache and build the sample application.

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 is an example of this.

If unfamiliar with nix, 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 a cabal file

➜  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 a default.nix (this will fetch a recent version of miso). miso will provide you with a working nixpkgs named pkgs. callCabal2nix will 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 from Sample Application to app/Main.hs

Build the project

nix-build

Open the result

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

For development with nix, it can be nice to have cabal present for building. This command will make it available in your PATH.

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

To be put into a shell w/ GHCJS and all the dependencies for this project present, use nix-shell.

nix-shell -A env

To view the dependencies for your project, call ghcjs-pkg list when inside the shell.

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

To build the project with cabal after entering the nix-shell

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

For incremental development inside of the nix-shell we recommend using a tool like entr to automatically rebuild on file changes, or roll your own solution with inotify.

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

Architecture

For constructing client and server applications, we recommend using one cabal file with two executable sections, where the buildable attribute set is contingent on the compiler. An example of this layout is here. For more info on how to use stack with a client/server setup, see this link. For more information on how to use nix with a client/server setup, see the nix scripts for https://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
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- | Haskell module declaration
module Main where

-- | Miso framework import
import Miso
import Miso.String

-- | Type synonym for an application model
type Model = Int

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

-- | Entry point for a miso application
main :: IO ()
main = startApp App {..}
  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 -> Effect Action Model
updateModel action m =
  case action of
    AddOne
      -> noEff (m + 1)
    SubtractOne
      -> noEff (m - 1)
    NoOp
      -> noEff m
    SayHelloWorld
      -> m <# do consoleLog "Hello World" >> pure NoOp

-- | Constructs a virtual DOM from a model
viewModel :: Model -> View Action
viewModel x = div_ [] [
   button_ [ onClick AddOne ] [ text "+" ]
 , text (ms x)
 , button_ [ onClick SubtractOne ] [ text "-" ]
 ]

Transition application

An alternative, more powerful interface for constructing miso applications is using the Transition interface. Transition is based on the StateT monad transformer, and can be used to construct components. It also works very nicely with lenses based on MonadState (i.e. (.=), (%=),(+=),(-=)).

-- | Haskell language pragma
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

-- | Haskell module declaration
module Main where

-- | Miso framework import
import Miso
import Miso.String

-- | Lens import
import Control.Lens

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

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

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

-- | Entry point for a miso application
main :: IO ()
main = startApp App {..}
  where
    initialAction = SayHelloWorld -- initial action to be executed on application load
    model  = Model 0              -- 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 -> Transition Action Model ()
updateModel action =
  case action of
    AddOne
      -> counter += 1
    SubtractOne
      -> counter -= 1
    NoOp
      -> pure ()
    SayHelloWorld
      -> scheduleIO_ (consoleLog "Hello World")

-- | Constructs a virtual DOM from a model
viewModel :: Model -> View Action
viewModel x = div_ [] [
   button_ [ onClick AddOne ] [ text "+" ]
 , text . ms $ x^.counter
 , button_ [ onClick SubtractOne ] [ text "-" ]
 ]

Live reload with JSaddle

It is possible to build miso applications with ghcid, jsaddle that allow live reloading of your application in reponse to changes in application code. See the README in the sample-app-jsaddle folder for more information.

Docker

Developing miso applications inside a Docker container is supported (allows applications to be built on Windows). See the README in the docker folder for more information.

Building examples

The easiest way to build the examples is with the nix package 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 named result

➜  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 is diff.js. It is responsible for all DOM manipulation that occurs in a miso application and has 100% code coverage. Tests and coverage made possible using jsdom and jest.

To run the tests and build the coverage report:

cd miso/tests
npm i
npm run test
## Or by using `yarn` instead of `npm`:
# yarn
# yarn test

Isomorphic

Isomorphic javascript is 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.

The miso function is used to perform the pointer-copying behavior client-side.

For more information on how miso handles isomorphic javascript, we recommend this tutorial.

Pinning nixpkgs

By default miso uses a known-to-work, pinned version of nixpkgs.

Binary cache

nix users on a Linux or OSX distro can take advantage of a binary cache for faster builds. To use the binary cache follow the instructions on cachix.

cachix use miso-haskell

Benchmarks

According to benchmarks, miso is among the fastest functional programming web frameworks, second only to Elm.

Maintainers

@dmjio

Commercial Users

Contributing

Feel free to dive in! Open an issue or submit PRs.

See CONTRIBUTING for 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

miso's People

Contributors

3noch avatar adrianomelo avatar aisk avatar basvandijk avatar brandonhamilton avatar cocreature avatar dependabot[bot] avatar dmjio avatar fptje avatar freeman42x avatar jamieballingall avatar jhrcek avatar juliendehos avatar lermex avatar niteria avatar noinia avatar parthshah31 avatar pidelport avatar ptigwe avatar rihardsk avatar robertfischer avatar roelvandijk avatar sectore avatar srid avatar tjlaxs avatar tolgap avatar troibe avatar tysonzero avatar woody88 avatar y-taka-23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

miso's Issues

Firefox shows deprecation warning

In Firefox I see this in the logs:

15:47:29.470 Use of getPreventDefault() is deprecated.  Use defaultPrevented instead.13698d11fd2ecc778cec5d5c8b2eb2c253f0d2f76.min.js:1:46930

Make all Effects asynchronous

update (Check id' isCompleted) model@Model{..} =
  eff <# model { entries = newEntries }
    where
      eff =
        putStrLn "clicked check" >>
          pure NoOp

putStrLn should execute in a an asynchronous thread.

Attribute diffing is broken in rare scenarios

I can't easily come up with a minimal demo for this bug, but in my case I implemented a draggable column on a table. One of the columns contained nothing but img tags. When I drag that column around, the image suddenly disappears. The DOM shows that the img tag still exists but doesn't have any attributes (even though I'm setting them in the view). When I wrap the img tag in some other tag (like span) everything works again.

Add a type level config

This should allow users to perform actions on each step of the FRP framework. Useful for debugging, or synchronizing the state with server, remote acid-state, saving to local storage

3 way merge `value` property

On new actions, if the value of an Element is already equal to the current value set in the Action, don't change the property. Otherwise, inspect current DOM value, new action value and old input value (on vdom), to decide which value must be set.

Add a type level config

This should allow users to perform actions on each step of the FRP framework. Useful for debugging, or synchronizing the state with server, remote acid-state, saving to local storage

GHCJS fails to install from a clean stack setup

I just checked out a clean version of the repo and tried stack setup. It fails after compiling all dependencies with the following error message:

The cabal-install found on PATH, version 1.22.9.0, is >= 1.22.8.
That version has a bug preventing ghcjs < 0.2.0.20160413 from booting.
See this issue: https://github.com/ghcjs/ghcjs/issues/470
Building a local copy of cabal-install from source.
Installed version of cabal-install is in a version range which may not work.
See this issue: https://github.com/ghcjs/ghcjs/issues/470
This version is specified by the stack.yaml file included in the ghcjs tarball.

fatal: program node is required but could not be found at node
Booting GHCJS (this will take a long time) ...Process exited with ExitFailure 1: /Users/bkc/.stack/programs/x86_64-osx/ghcjs-0.2.0.820160417_ghc-7.10.3/src/.stack-work/install/x86_64-osx/nightly-2016-04-17/7.10.3/bin/ghcjs-boot --clean

It looks like we need to put an upper bound on the version of cabal-install. I'm not sure how stack didn't pick up on this because the bound is specified in the GHCJS stack.yaml. It looks like ghcjs/ghcjs#470 may be relevant. It seems they have a solution there, should we update the stack.yaml for this?

miso-from-html

This would be a /nice to have/, similar to blaze-from-html

Add tests

Things to test:

  • Correct recognition and application of different patching operations
    • Remove patch
    • Insert patch
    • Keys patch
  • Virtual event propagation can be stopped
  • Go from VTree <-> DOM (isomorphic js) - caveat: must be done in I/O
    • Check the references are correct
  • Lucid HTML instance
  • Property diffing
  • Node rendering

Recommendation: style_ becomes styleRaw_ and style_ mimics Elm

Elm's style function takes a list of tuples (key, value). This is convenient when writing styles yourself. Of course, I can see wanting raw style strings as well so might as well have a styleRaw_ variant. We may prefer to take Map (CI Text) Text instead to prevent the same style being set twice.

Miso Logo

I tried using http://fiver.com for a custom logo and it went awful. Ideally miso would have a white bowl with miso soup and a tofu that is in the shape of a lambda.

rewrite `datch` with vector

In order to minimize allocations, I believe rewriting datch (and potentially altering the structure of VTree would go a long way.

From @luite

<luite_> linked list is one object (= javascript object) per cons cell, a vector is a single JS array for the whole array.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.