shpadoinkle :: Backend b m a => Territory t => Eq a
=> (m ~> JSM) -> (t a -> b m ~> m) -> a -> t a -> (a -> Html (b m) a) -> b m RawNode -> JSM ()
Backends
Render backends are pluggable in Shpadoinkle. This project currently comes with three backends:
ParDiff |
|||
Snabbdom |
|||
Static |
You do not need to change your application to use any of these. Our view code will work without alteration with any backend. The application should still build and run with both GHC and GHCjs.
Using a Backend
Take a quick look at the shpadoinkle
primitive:
The second argument (t a → b m ~> m)
is how you choose your backend. For example:
import Shpadoinkle.Backend.ParDiff
shpadoinkle id runParDiff 0 territory view getBody
This will use the ParDiff Virtual DOM backend written in Haskell by simply changing it to:
import Shpadoinkle.Backend.Snabbdom
shpadoinkle id runSnabbdom 0 territory view getBody
Now the application will render with the Snabbdom Virtual DOM backend written in JavaScript instead.
The Static backend is a bit different as it does not work with the
|
Writing a Backend
Adding your own backend is a matter of writing an instance of the Backend
type class located in core.
class Backend b m a | b m -> a where
type VNode b m
interpret :: (m ~> JSM) -> Html (b m) a -> b m (VNode b m)
patch :: RawNode -> Maybe (VNode b m) -> VNode b m -> b m (VNode b m)
setup :: JSM () -> JSM ()
This interface lets you plug into various rendering systems. So long as you can provide implementations of these three functions, you can use shpadoinkle
to get an application out of Html
.
This packages does not come with a backend implementation, and an implementation is required to run the shpadoinkle
function.
Monad Transformer
|
VNode
This type family points maps to the underlying representation native to the backend:
type VNode b m
In the case of binding to a JavaScript library, this would most likely be a newtype
of JSVal
. When binding to a typed implementation, this should just be set to the library type.
Interpret
This function describes how to marshal between Html
and the native representation (i.e. VNode
):
interpret
:: (m ~> JSM) (1)
-> Html (b m) a (2)
-> b m (VNode b m) (3)
The interpret function can be Monadic, as it is likely going to require IO to obtain the native representation.
1 | Interpret is provided with a mechanism for getting from the end user provided Monad to JSM directly. |
2 | The Html Shpadoinkle view that needs to be marshalled to the native representation for this backend. |
3 | A Monadic action that generates VNode . |
Patch
This function describes how updates are handled:
patch
:: RawNode (1)
-> Maybe (VNode b m) (2)
-> VNode b m (3)
-> b m (VNode b m) (4)
The interpret function can be Monadic, as it is likely going to require IO to apply the new VNode
to the view.
1 | This is the parent DOM Node that contains the application. RawNode is a newtype of JSVal . |
2 | The previously rendered VNode . On the first rendering of the application, this will be Nothing . |
3 | The VNode the user would like to render. |
4 | A Monadic action that actually renders in the browser and returns a new VNode . The returned (v :: VNode ) will be (Just v ) for 2 in the next render. |
Setup
This is an optional IO action to perform any initial setup steps a given backend might require:
setup
:: JSM () (1)
-> JSM ()
1 | This is a callback you are responsible for executing after the setup process is complete. The callback is the entire application. If you do not evaluate the JSM () , then nothing will happen. |
In the case of JavaScript-based backends, it will likely include steps like adding the library to the <head>
of the page, or instantiating a JavaScript class.