Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/misoPublic

🍜 A tasty Haskell web framework

License

NotificationsYou must be signed in to change notification settings

dmjio/miso

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

AtastyHaskell web and mobile framework 🍜

Matrix #haskell-miso:matrix.orgHaskellCachixBuild StatusHackageLICENSE

Miso is a small, production-ready, component-oriented,isomorphicHaskell front-end framework for quickly building highly interactive single-page web andmobile applications. It features a virtual-dom, recursive diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, Server-sent events (SSE),Websockets, type-safeservant-style routing and an extensible Subscription-based subsystem. Inspired byElm andReact.Miso is pure by default, but side effects can be introduced into the system via theEffect data type.Miso makes heavy use of theGHC Javascript FFI and therefore has minimal dependencies.Miso can be considered a shallowembedded domain-specific language for modern web programming.

Miso supports compilation to bothJavaScript andWebAssembly usingGHC. For hot-reload,miso uses thejsaddle library. When used withghcid andghciwatch this enables a rapid development workflow.

Tip

React-style Components are now added tomiso as of version1.9. This has not yet been released, we recommend developing againstmaster if you'd like to use latest features.

Table of Contents

History 📜

miso is a play on the wordsmicro andisomorphic.

miso began in 2016 as research in:

Miso aims tobridge the gap between modern JavaScript techniques (as found inReact,Vue.js, etc.) and functional programming inHaskell.

It has since grown to encompass more techniques from the JavaScript community likeComponents andRenderers. It alsonow supports native development to iOS / Android viaLynxJS and additional backends likeWeb Assembly.

Quick start

To start developing applications withmiso you will need to acquireGHC andcabal. This can be done viaGHCup orNix.

Tip

For new Haskell users we recommend usingGHCup to acquire bothGHC andcabal

Setup 🏗️

To develop and build your firstmiso application you will need 3 files:

  • cabal.project
  • app.cabal
  • Main.hs

cabal.project

packages:.source-repository-packagetype:gitlocation:https://github.com/dmjio/misobranch:master

app.cabal

We recommend using at leastcabal-version: 2.2, this will give you thecommon sections feature which we will use later to allow multiple compilers to build our project (so we can targetWASM andJS backends)

cabal-version:2.2name:appversion:0.1.0.0synopsis:Sample miso appcategory:Webcommon wasmif arch(wasm32)ghc-options:-no-hs-main-optl-mexec-model=reactor"-optl-Wl,--export=hs_start"cpp-options:-DWASMexecutable appimport:wasmmain-is:Main.hsbuild-depends:base, misodefault-language:Haskell2010

Main.hs

This file contains a simplemiso counter application.

----------------------------------------------------------------------------{-#LANGUAGE OverloadedStrings #-}{-#LANGUAGE RecordWildCards   #-}{-#LANGUAGE LambdaCase        #-}{-#LANGUAGE CPP               #-}----------------------------------------------------------------------------moduleMainwhere----------------------------------------------------------------------------importMisoimportMiso.StringimportMiso.Lens------------------------------------------------------------------------------| Component model statenewtypeModel=Model{_counter::Int}deriving (Show,Eq)----------------------------------------------------------------------------counter::LensModelIntcounter= lens _counter$\record field-> record { _counter= field }------------------------------------------------------------------------------| Sum type for Component eventsdataAction=AddOne  |SubtractOne  |SayHelloWorldderiving (Show,Eq)------------------------------------------------------------------------------| Entry point for a miso applicationmain::IO()main= run (startComponent app)------------------------------------------------------------------------------| WASM export, required when compiling w/ the WASM backend.#ifdef WASMforeignexport javascript"hs_start" main::IO()#endif------------------------------------------------------------------------------| `component` takes as arguments the initial model, update function, view functionapp::ComponentModelActionapp= component emptyModel updateModel viewModel------------------------------------------------------------------------------| Empty application stateemptyModel::ModelemptyModel=Model0------------------------------------------------------------------------------| Updates model, optionally introduces side effectsupdateModel::Action->EffectModelActionupdateModel=\caseAddOne-> counter+=1SubtractOne-> counter-=1SayHelloWorld-> io_$do    consoleLog"Hello World"    alert"Hello World"------------------------------------------------------------------------------| Constructs a virtual DOM from a modelviewModel::Model->ViewActionviewModel x= div_[]  [ button_ [ onClickAddOne ] [ text"+" ]  , text$ ms (x^. counter)  , button_ [ onClickSubtractOne ] [ text"-" ]  , button_ [ onClickSayHelloWorld ] [ text"Alert Hello World!" ]  ]----------------------------------------------------------------------------

Now that your project files are populated, development can begin.

Hot Reload 🔥

WithGHC andcabal on$PATH, callcabal repl

$ cabal repl

You should see the following output in your terminal.

[1 of 2] Compiling Main             ( Main.hs, interpreted )Ok, one module loaded.ghci>

Now call themain function in theGHCi REPL.

ghci> mainRunning on port 8008...<a href="http://localhost:8008">run</a>ghci>

Note

The code running in this example is not compiled to JavaScript or WebAssembly, rather it is running the client side application on the server. It works by sending commands to a small javascript interpreter over a websocket to render elements on the page. This is provided by thejsaddle library.

If you visithttp://localhost:8008, the application will be live. You can now editMain.hs, call:r andmain inGHCi, and the application will update on the screen.

Note

Instead of typing:r andmain manually inside ofGHCi on every file change, you can useghcid orghciwatch tools to do it automatically.

Tip

For users accustomed to a react.js worfklow, we highly recommend using eitherghcid orghciwatch.

Below is an example of usage withghcid

$ ghcid -c'cabal repl app' -T=Main.main

This screenshot shows the hot-reload functionality in action. This is usingghcid,jsaddle andmiso.

Image

Compilation

When done developing, we can compile to Web Assembly or JavaScript for distribution. This is done by acquiring aGHC that supports WebAssembly or JavaScript. We recommend acquiring these backends usingGHCUp orNix.

Tip

For new Haskell users we recommend usingGHCup to acquire theWASM andJS backends.

Image Web Assembly

Tip

The Haskellmiso team currently recommends using the WASM backend as the default backend for compilation.

UsingGHCup you should be able to acquire theGHCWASM compiler.

For instructions on how to add a third-party channel withGHCup, please see their officialREADME.md

Tip

ForNix users it is possible to acquire the WASM backend via aNix flake

$ nix shell'gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org'

Note

This will putwasm32-wasi-cabal in your$PATH, along withwasm32-wasi-ghc. Since the WASM backend is relatively new, the ecosystem is not entirely patched to support it. Therefore, we will need to use patched packages from time to time.

Tip

Instead of using anix shell, it's possible to install the GHC WASM Flake into your environment so it will always be present on$PATH

$ nix profile install'gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org'

Update yourcabal.project to the following

  • cabal.project
packages:.with-compiler:wasm32-wasi-ghcwith-hc-pkg:wasm32-wasi-ghc-pkgsource-repository-packagetype:gitlocation:https://github.com/dmjio/misobranch:masterif arch(wasm32)-- Required for TemplateHaskell. When using wasm32-wasi-cabal from-- ghc-wasm-meta, this is superseded by the global cabal.config.shared:True

Callwasm32-wasi-cabal build --allow-newer and aWASM payload should be created indist-newstyle/ directory.

$ wasm32-wasi-cabal update$ wasm32-wasi-cabal build --allow-newer
Configuration is affected by the following files:- cabal.projectResolving dependencies...Build profile: -w ghc-9.12.2.20250327 -O1In order, the following will be built (use -vfor more details): - app-0.1.0.0 (exe:app) (configuration changed)Configuring executable'app'for app-0.1.0.0...Preprocessing executable'app'for app-0.1.0.0...Building executable'app'for app-0.1.0.0...[1 of 1] Compiling Main             ( Main.hs, dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app-tmp/Main.o )[2 of 2] Linking dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app.wasm

You have now successfully compiled a Haskellmiso application to WebAssembly 🔥


But, we're not done yet. In order to view this in the browser there are still a few more steps. We need to add some additional files that emulate theWASI interface in the browser (A browser WASI shim).

Note

The GHC WASM backend can execute any Haskell program in a WASI-compliant runtime (e.g.wasmtime)See theofficial documentation for more information.

To start, we recommend creating anapp.wasmexe folder to store the additional artifacts required.

Tip

We recommend using an up-to-datenode version (currently tested withv24.2.0) to ensurepost-link.mjs works properly.

# Creates the directory for hosting$ mkdir -v app.wasmexemkdir: created directory'app.wasmexe'# This command produces `ghc_wasm_jsffi.js`, which ensures our FFI works properly.$$(wasm32-wasi-ghc --print-libdir)/post-link.mjs \   --input$(wasm32-wasi-cabal list-bin app --allow-newer) \   --output app.wasmexe/ghc_wasm_jsffi.js# This copies the `app.wasm` payload into `app.wasmexe`$ cp -v$(wasm32-wasi-cabal list-bin app --allow-newer) app.wasmexeConfiguration is affected by the following files:- cabal.project'/home/dmjio/Desktop/miso/sample-app/dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app.wasm' ->'app.wasmexe'

Note

Along with the aboveghc_wasm_jsffi.js andapp.wasm artifacts, we also need to include anindex.html and anindex.js for loading the WASM payload into the browser.

  • index.html
<!DOCTYPE html><html><head><metacharset="utf-8"><metaname="viewport"content="width=device-width, initial-scale=1"><title>Sample miso WASM counter app</title></head><body><scriptsrc="index.js"type="module"></script></body></html>
  • index.js
import{WASI,OpenFile,File,ConsoleStdout}from"https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.3.0/dist/index.js";importghc_wasm_jsffifrom"./ghc_wasm_jsffi.js";constargs=[];constenv=["GHCRTS=-H64m"];constfds=[newOpenFile(newFile([])),// stdinConsoleStdout.lineBuffered((msg)=>console.log(`[WASI stdout] ''${msg}`)),ConsoleStdout.lineBuffered((msg)=>console.warn(`[WASI stderr] ''${msg}`)),];constoptions={debug:false};constwasi=newWASI(args,env,fds,options);constinstance_exports={};const{ instance}=awaitWebAssembly.instantiateStreaming(fetch("app.wasm"),{wasi_snapshot_preview1:wasi.wasiImport,ghc_wasm_jsffi:ghc_wasm_jsffi(instance_exports),});Object.assign(instance_exports,instance.exports);wasi.initialize(instance);awaitinstance.exports.hs_start(globalThis.example);

Theapp.wasmexe folder will now look like:

❯ ls app.wasmexe app.wasm ghc_wasm_jsffi.js index.html index.js

Now you can host and view theapp.wasm payload in a web browser.

$ http-server app.wasmexe

Tip

You can inspect the WASM payload in theSources tab of your browser by right-clicking and then clickingInspect.

Image

JavaScript

UsingGHCup you should be able to acquire the latest GHC JS-backend compiler.

Tip

ForNix users it is possible to acquire the latest JS backend (thatmiso uses) via Nix.Usecachix to ensure you're not building dependencies unnecessarilycachix use haskell-miso-cachix

nix-shell -p pkgs.pkgsCross.ghcjs.haskell.packages.ghc9122.ghc -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/65f179f903e8bbeff3215cd613bdc570940c0eab.tar.gz

Note

This will putjavascript-unknown-ghcjs-ghc in your$PATH, along withjavascript-unknown-ghcjs-ghc-pkg. You might also need to specify in yourcabal.project file that you are using the JS backend.

Tip

Alternatively, if you'd like to install the compiler into your global environment (so you don't need to develop inside abash shell) you can use the following command.

nix-env -iA pkgs.pkgsCross.ghcjs.haskell.packages.ghc9122.ghc -f https://github.com/NixOS/nixpkgs/archive/65f179f903e8bbeff3215cd613bdc570940c0eab.tar.gz
  • cabal.project
packages:.source-repository-packagetype:gitlocation:https://github.com/dmjio/misobranch:masterwith-compiler:javascript-unknown-ghcjs-ghcwith-hc-pkg:javascript-unknown-ghcjs-ghc-pkg

Note

cabal will use theghc specified above inwith-compiler

$ cabal update&& cabal build --allow-newer
Configuring executable'app'for app-0.1.0.0...Preprocessing executable'app'for app-0.1.0.0...Building executable'app'for app-0.1.0.0...[1 of 1] Compiling Main             ( Main.hs, dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app-tmp/Main.o )[2 of 2] Linking dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app.jsexe

Tip

To view the JavaScript in your browser, you can usecabal list-bin andhttp-server

$ http-server$(cabal list-bin app --allow-newer).jsexeConfiguration is affected by the following files:- cabal.projectStarting up http-server, serving /home/dmjio/Desktop/miso/sample-app/dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app.jsexehttp-server version: 14.1.1http-server settings:CORS: disabledCache: 3600 secondsConnection Timeout: 120 secondsDirectory Listings: visibleAutoIndex: visibleServe GZIP Files:falseServe Brotli Files:falseDefault File Extension: noneAvailable on:  http://127.0.0.1:8080  http://192.168.1.114:8080Hit CTRL-C to stop the server

Haddocks

OfficalHaskell documentation of theMiso web framework.

PlatformURL
GHCJSLink
GHCLink

Architecture

For constructing client and server applications, we recommend using onecabal file with two executable sections, where thebuildable attribute set is contingent on the compiler. An example of this layout ishere.

Tip

For more information on how to usenix with aclient/server setup, see thenix scripts forhttps://haskell-miso.org.

Internals ⚙️

For some details of the internals and general overview of howmiso works, see theInternals.

Examples

For real-world examples of Haskellmiso applications, see below.

NameDescriptionSource LinkLive Demo LinkAuthor
TodoMVCA classic TodoMVC implementationSourceDemo@dmjio
2048A clone of the 2048 gameSourceDemo@ptigwe
FlatrisA Tetris-like gameSourceDemo@ptigwe
PlaneA flappy-birds-like gameSourceDemo@Lermex
SnakeThe classic Snake gameSourceDemo@lbonn
SVGAn example showcasing SVG renderingSourceDemo@dmjio
FetchAn example demonstrating AJAX requestsSourceDemo@dmjio
File ReaderA FileReader API exampleSourceDemo@dmjio
Three.jsA 3D rendering example using Three.JSSourceDemo@juliendehos
MarioA Super Mario physics exampleSourceDemo@dmjio
WebSocketA simple WebSocket exampleSourceDemo@dmjio
RouterA client-side routing exampleSourceDemo@dmjio
Canvas 2DA 2D Canvas rendering exampleSourceDemo@dmjio
Space InvadersA Space-Invaders-like gameSourceDemo@juliendehos
AudioAudio examplesSourceDemo@juliendehos
VideoVideo examplesSourceDemo@juliendehos

Building examples

The easiest way to build the examples is with thenix package manager

Tip

Usecachix to ensure you're not building dependencies unnecessarilycachix use haskell-miso-cachix

$ git clone https://github.com/dmjio/miso$ cd miso$ nix-build -A miso-examples

This will compile all the examples to JavaScript into a folder namedresult.

➜ tree -d ./result/bin./result/bin|-- canvas2d.jsexe|-- file-reader.jsexe|-- mario.jsexe|   `-- imgs|-- mathml.jsexe|-- router.jsexe|-- simple.jsexe|-- svg.jsexe|-- tests.jsexe|-- threejs.jsexe|-- todo-mvc.jsexe|-- websocket.jsexe`-- fetch.jsexe

Note

To see examples, we recommend hosting them with a web server (we usehttp-server)

cd result/bin/todo-mvc.jsexe && http-severServing HTTP on 0.0.0.0 port 8000 ...

Interacting with HTTP APIs 🔌

If you want to interact with an HTTP API, we recommend one of the following approaches:

  1. For a simple JSON-based API, you can use Miso'sfetch function.

  2. In more complex cases, you can define aServant API and automatically obtain client functions viaservant-client-js (or any otherservant-client-core-based backend).

    The Fetch example (Source,Demo) demonstrates the necessary ingredients. Make sure to add the following to yourcabal.project:

    source-repository-packagetype: gitlocation: https://github.com/amesgen/servant-client-jstag: 2853fb4f26175f51ae7b9aaf0ec683c45070d06e

Coverage ✅

The core engine ofmiso is thediff function. It is responsible for all DOM manipulation that occurs in a miso application and has100% code coverage. Tests and coverage made possible usingbun.

Note

To run the tests and build the coverage report ensurebun is installed.

$ curl -fsSL https://bun.sh/install| bash

or

$ nix-env -iA bun -f'<nixpkgs>'

and

$ bun install&& bun runtest
--------------------|---------|---------|-------------------File| % Funcs| % Lines| Uncovered Line#s--------------------|---------|---------|-------------------All files|   92.37|   85.48| ts/happydom.ts|  100.00|  100.00| ts/miso/dom.ts|  100.00|  100.00| ts/miso/event.ts|   90.91|   81.62| ts/miso/hydrate.ts|   80.00|   91.24| ts/miso/smart.ts|  100.00|  100.00| ts/miso/util.ts|   83.33|   40.00|--------------------|---------|---------|------------------- 84 pass 0 fail

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 (a process known ashydration), and the application proceeds as normal. All subsequent page navigation is handled locally by the client, while avoiding full-page postbacks.

Note

Themiso function is used to facilitate the pointer-copying behavior client-side.

Native📱

Miso supports the creation of iOS and Android applications viaLynxJS. See themiso-lynx repository for more information.

Benchmarks 🏎️

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

Nixnixos-snowflake

Nix is a powerful option for building web applications withmiso since it encompasses development workflow, configuration management, and deployment. The source code forhaskell-miso.org is an example of this.

Tip

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

Pinning nixpkgs 📌

By defaultmiso uses a known-to-work, pinned version ofnixpkgs known aspkgs.

Note

miso also maintains a legacy version of nixpkgs known aslegacyPkgs so we can use tools likenixops for deployment and to buildmiso with the originalGHCJS 8.6 backend.

Binary cache

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

Tip

We highly recommend nix users consume thecachix cache.cachix use haskell-miso-cachix.

$ cachix use haskell-miso-cachix

Community 🫶

Maintainers

@dmjio

Contributing

Feel free to dive in!Open an issue or a submitPull Request.

SeeCONTRIBUTING for more info.

Contributors 🦾

Note

This project exists thanks to all the people whocontribute

Partnerships 🤝

If you'd like to support this project financially, be it through requesting feature development, or a corporate partnership, please drop us a line and we will be in touch shortly.

support@haskell-miso.org

Backers

Become afinancial contributor and help us sustain our project and community. We are very grateful and thankful for our individual sponsors.

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. We are also very grateful and thankful for our corporate sponsors.

License

BSD3 © dmjio


[8]ページ先頭

©2009-2025 Movatter.jp