When I released web-view 6 months ago, I said I was “weeks” away from releasing a framework for interactive web apps built on top of it. Well it’s been 26 weeks, and it’s finally ready!
Hyperbole makes it easy to create fully interactive HTML applications with type-safe serverside Haskell. It’s inspired by HTMX, Elm, and Phoenix LiveView
Motivation
I’ve been a web developer since before “Ajax”. I rode the wave of Single Page Applications (SPAs) and loved how interactive we could make things. I’ve written fancy apps in React and Elm. But ultimately SPAs mean writing two applications, a Javascript client and a server, plus an API between them. They’re a huge pain to write and maintain. I missed serverside web apps.
Instead of an SPA, Hyperbole allows us instead to write a single Haskell program which runs exclusively on the server. All user interactions are sent to the server for processing, and a sub-section of the page is updated with the resulting HTML.
There are frameworks that support this in different ways, including HTMX, Phoenix LiveView, and others. Hyperbole has the following advantages
- 100% Haskell
- Type safe views, actions, routes, and forms
- Elegant interface with little boilerplate
- VirtualDOM updates over sockets, fallback to HTTP
- Easy to use
Like HTMX, Hyperbole extends the capability of UI elements, but it uses Haskell’s type-system to prevent common errors and provide default functionality. Specifically, a page has multiple update targets called HyperView
s. These are automatically targeted by any UI element that triggers an action inside them. The compiler makes sure that actions and targets match.
Like Phoenix LiveView, it upgrades the page to a WebSocket connection and uses VirtualDOM for live updates
Like Elm, it relies on an update function to handle
actions, but greatly simplifies the Elm Architecture by handling state with extensible effects. form
s are easy to use with minimal boilerplate
Depends heavily on the following frameworks
Simple Example
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
import Web.Hyperbole
main = do
run 3000 $ do
liveApp (basicDocument "Example") (page mainPage)
mainPage = do
handle message
load $ do
pure $ do
el bold "My Page"
hyper (Message 1) $ messageView "Hello"
hyper (Message 2) $ messageView "World!"
data Message = Message Int
deriving (Generic, Param)
data MessageAction = Louder Text
deriving (Generic, Param)
instance HyperView Message where
type Action Message = MessageAction
message :: Message -> MessageAction -> Eff es (View Message ())
message _ (Louder m) = do
let new = m <> "!"
pure $ messageView new
messageView m = do
el_ $ text m
button (Louder m) id "Louder"
Learn More
Hackage has a better intro and good docs
Examples demonstrating different features
Feedback
Any questions and comments appreciated! Please let me know if anything isn’t clear from the docs.
(Cross Posted to Reddit)