Interactive, client-side web apps in Haskell
Shade is a DSL for writing browser-side applications in Haskell without the need for callbacks or pervasive mutation.
Shade is currently quite experimental. Drastic changes are to be expected
phrase = "Hello, World!" helloInput :: (Shade s) => String -> s (Async s String) helloInput str = do (inpAsync, inp) <- letElt (input [value (fromString valid)]) div  $ do h1  (text ("Enter '"++phrase++"': "++valid++"\n")) inp return (fmap (toString . changeEventValue) (onChange inpAsync)) where valid = map fst (takeWhile (uncurry (==)) (zip str phrase)) mainLoop elt str = do (a, r) <- runClient (helloInput str) listen a (\s -> mainLoop elt s) renderClient elt r main :: IO () main = do Just e <- elemById "reactroot" mainLoop e ""
What is Shade?
Shade offers a purely functional approach to specifying interactive web applications. Instead of consisting of two-way data bindings between GUI widgets, applications are conceived as pure, one-way functions from a model to a view. As the model changes, the view is simply re-rendered. Shade uses React to make this repeated re-rendering extremely efficient.
Applications are specified with Shade by using a typed tagless-final domain-specific syntax. A key advantage of the tagless-final style is its solution to the expression problem. This means you can add more terms to your syntax (allowing, for example, custom html tags in the style of Angular's Directives) while, at the same time, interpreting the same syntax in various ways (for example, generating both a static file on the server side and an interactive app on the client side without changing the application code). Shade consists of the platform-agnostic
shade-core, which is the tagless-final markup syntax and
Shade eschews callbacks and mutation in favour of values and composition. Rendered components can be treated as Async-like values that represent the delivery of an event at some point in the future. These can be mapped over like any other functor or merged together like a monoid. Shade's use of Asyncs allows for highly-composable interactive behaviours in a similar vein as those offered by Functional Reactive Programming or Reactive Extensions.
Slides are available for a talk introducing Haste and tagless-final style.
shade-haste by running
haste-inst install in their respective subdirectories.
To use shade in a new app, add
shade-haste to your .cabal file's
ghc-options: --with-js=<...>/stubs.js, where
<...>/stubs.js is the path to the stub file
haste-inst build && mv src/*.js js/