# Composing Heterogeneous Elements

Composing HTML elements is straightforward in Shpadoinkle when the elements all have the same model type; nesting them in each other works automatically.

For an element to be a child of another element, they must have the same model type. This means that all elements in the tree under an `Html m Model`

(children, children of children, etc.) must have the type `Html m Model`

. Therefore, before an element can become the child of a parent, it must be transformed to have a matching model type, if the model types do not already match.

Changing the model type of an HTML element can be accomplished using the `MC`

functions which make use of the `MapContinuations`

typeclass. This typeclass is instantiated by types where the values can be changed by functions which change continuations.

Continuations are a concept in Shpadoinkle’s event handling system. Each event handler causes a continuation to be run. That continuation may perform I/O actions and/or update the model. If a continuation updates the model, then it does so atomically, which means that it is safe to have multiple continuations running concurrently. To learn more about continuations, see the Shpadoinkle-continuations package. |

First consider the case where you have an element `x :: Html m a`

and you need to change its type to `Html m (a,b)`

so that the event handlers act on the left side of the tuple. In this case you should use the `leftMC`

function: `leftMC x :: Html m (a,b)`

.

Similarly, if you have an element `x :: Html m b`

and you need to change its type `Html m (a,b)`

, then you should use the `rightMC`

function: `rightMC x :: Html (a,b)`

.

If you have an element `x :: Html m ()`

then you can freely vary its model type using `voidC`

: `voidC x :: Html m a`

. The event handlers will have the same side effects and they will not touch the model.

You can change the model type from anything to anything using the `forgetMC`

function, which will strip out all event handlers, replacing them with no-ops. For any types `a`

, `b`

and monads `m`

, `n`

, if `x :: Html m a`

then `forgetMC x :: Html n b`

.

Given a lens from a type `b`

to a type `a`

, consisting of a function `set :: (a -> b -> b)`

and a function `get :: b -> a`

, you can change an element `x :: Html m a`

to an element `liftMC set get x :: Html m b`

using the `liftMC`

function.

You can change an element `x :: Html m a`

to an element `maybeMC x :: Html m (Maybe a)`

, where the event handlers are lifted into the `Maybe`

monad using `fmap`

(so they will have the same effect when the model is `Just`

and they will not affect the model when the model is `Nothing`

).

Given an isomorphism, you can change the type of an element from one side of the isomorphism to the other side. An isomorphism from a type `a`

to a type `b`

consists of a function `f :: a -> b`

and a function `g :: b -> a`

such that `f . g = id`

and `g . f = id`

. You can change an element `x :: Html m a`

in the following way: `pimap (piiso f g) x :: Html m b`

.

There is one more way of changing the model type of an element which you are likely to need, which is lifting into coproducts. Let’s say that you have two views in your app, represented by models of types `a`

and `b`

respectively. Then you can have an overall model of type `Either a b`

. How can you combine elements `x :: Html m a`

and `y :: Html m b`

into the same structure?

In this scenario it will make more sense to think of view functions as opposed to elements. By a view function, I mean a function which goes from the value of the model to an element. So let’s suppose we have view functions `v :: a -> Html m a`

and `u :: b -> Html m b`

. Then we can combine them as `eitherH v u :: Either a b -> Html m (Either a b)`

. This combined element will show the view `v`

when the model is a `Left`

and the view `u`

when the model is a `Right`

.

If you wish to have multiple views combined in this way but you do not want your model type to be an `Either`

type, then you can combine this coproduct pattern with an isomorphism. Suppose that `f :: Either a b -> Model`

and `g :: Model -> Either a b`

constitute an isomorphism, and `v`

and `u`

are as above. Then: `pimap (piiso f g) . eitherH v u . g :: Model -> Html m Model`

.

For examples of many of these patterns, see the Servant CRUD example.