miso
Copyright(C) 2016-2026 David M. Johnson
LicenseBSD3-style (see the file LICENSE)
MaintainerDavid M. Johnson <code@dmj.io>
Stabilityexperimental
Portabilitynon-portable
Safe HaskellNone
LanguageHaskell2010

Miso.FFI.QQ

Description

Overview

Miso.FFI.QQ provides the js quasi-quoter, which lets you embed JavaScript snippets directly in Haskell source. In-scope Haskell variables are spliced into the JS body with ${varName} interpolation syntax, and their types are checked at compile time via ToJSVal.

Enable the extension and import the quoter:

{-# LANGUAGE QuasiQuotes #-}
import Miso.FFI.QQ (js)

Quick start

-- Compute a factorial entirely in JavaScript
fac :: Int -> IO Int
fac n = [js|
  let x = 1;
  for (let i = 1; i <= ${n}; i++) {
    x *= i;
  }
  return x;
|]

-- Call a third-party JS library with a DOM reference and a string
highlight :: JSVal -> MisoString -> IO ()
highlight domRef lang = [js|
  hljs.highlightElement(${domRef}, { language: ${lang} });
|]

How it works

At compile time the quasi-quoter:

  1. Lexes the JS body to find all ${varName} interpolations.
  2. Looks up each varName in the Haskell scope (compile error if not found).
  3. Builds a Object mapping short generated keys to the marshalled values (via inline / createWith).
  4. Rewrites the JS body, replacing each ${varName} with its generated key, and wraps the whole thing in a JS function so the keys are visible as named parameters.

The result is semantically equivalent to:

do o <- createWith [("a0", toJSVal n)]
   inline "… body with a0 instead of n …" o

Differences from eval

Unlike eval, the generated code runs in a fresh function scope — it cannot read or write surrounding local variables other than those explicitly interpolated. This makes it both safer and faster (JS engines can optimise closed-over functions that don't reference eval).

See also

Synopsis

Documentation

js :: QuasiQuoter Source #

QuasiQuoter for specifying inline JavaScript.