Creating a Haskell Application Using Reflex. Part 2
Tags: haskell, functional, programming, tutorial
Published: 2021-04-12 15:13:09

Author: Nikita Anisimov

[Part 1](https://dev.to/typeable/creating-a-haskell-application-using-reflex-part-1-3kk3)

[Part 3](https://dev.to/typeable/creating-a-haskell-application-using-reflex-part-3-47d6)

[Part 4](https://dev.to/typeable/creating-a-haskell-application-using-reflex-part-4-4jn7)

Hi there! Let’s continue with our series of tutorials devoted to the development of Reflex-based web applications.
In this part, we’ll add the ability to carry out various manipulations on the task list.


![](https://blog.typeable.io/images/reflex/2_1.gif)

# Operations on Todo

Let’s make it possible to tick the tasks off as completed, as well as edit and delete them.
First of all, we extend the `Todo` type by adding the state. If the task is not completed, you can edit it.

```Haskell
data TodoState
  = TodoDone
  | TodoActive { stateEdit :: Bool }
  deriving (Generic, Eq, Show)

data Todo = Todo
  { todoText  :: Text
  , todoState :: TodoState }
  deriving (Generic, Eq, Show)

newTodo :: Text -> Todo
newTodo todoText = Todo { todoState = TodoActive False, .. }
```

After that, we define the events occurring in the system. In our business projects, we used two approaches to this end. The first approach implies enumerating all possible events as individual constructors and implementing the handler function that will update the state depending on the occurred event.

```Haskell
data TodoEvent
  = NewTodo Todo
  | ToggleTodo Int
  | StartEditTodo Int
  | FinishEditTodo (Text,Int)
  | DeleteTodo Int
  deriving (Generic, Eq, Show)
```

The advantages of this approach include the ability to see what event specifically is taking place in the system and the update it’s carrying (all this is done by the `traceEvent` function). Nevertheless, it’s not always possible to use the advantages, especially when the event carries a lot of data which is eventually hard to analyze. If you still need to see the values change, the events change `Dynamic` in any case and you can also trace its value using the function `traceDyn`.

The second approach is to use the update functions represented as the monoid [`Endo`](https://hackage.haskell.org/package/endo-0.3.0.1/docs/Data-Monoid-Endo.html#t:Endo) (roughly speaking, this is an abstraction of the functions whose argument  and result types coincide). The essence of this approach is that the value carried by the update event is the function that defines the update logic itself. In this case, we lose the ability to display the event value (as it turns out, this ability is not always useful), but the obvious advantage is that you don’t need to have access to the current status, create the type including all possible events (which can be quite numerous), or define an individual handler to update the state according to the received event.

In this tutorial, we will use the second approach.

Let’s change the structure of the root widget:

```Haskell
rootWidget :: MonadWidget t m => m ()
rootWidget =
  divClass "container" $ do
    elClass "h2" "text-center mt-3" $ text "Todos"
    newTodoEv <- newTodoForm
    rec
      todosDyn <- foldDyn appEndo mempty $ leftmost [newTodoEv, todoEv]
      delimiter
      todoEv <- todoListWidget todosDyn
    blank
```

What we see here in the first place is the use of the `RecursiveDo` extension (so you need to enable it). This is one of the most widespread practices in the development of `reflex` applications because the situations when the event occurring at the bottom of the page affects the elements at the top of the page happen pretty often. In this case, the event `todoEv` is used to define `todosDyn`, while `todosDyn`, in its turn, is the argument for the widget the `todoEv` event comes from.

After that, we see the update of the `foldDyn` function parameters. Here the new function `leftmost` is used. It accepts the events list and returns the event occurring at the moment when any of the events from the events list occurs. If two events from the list occur at a given moment, the leftmost event will be returned (hence the name). The task list is not a list now but [`IntMap`](https://hackage.haskell.org/package/containers-0.6.4.1/docs/Data-IntMap.html) (for the sake of simplicity, we’ll use `type Todos = IntMap Todo`).  In the first place, this is done to allow us to access an element directly by the identifier. `appEndo` is used to update the list. If we defined each event as an individual constructor, we’d have also had to define the handler function, which would look approximately as follows:

```Haskell
updateTodo :: TodoEvent -> Todos -> Todos
updateTodo ev todos = case ev of
  NewTodo todo           -> nextKey todos =: todo <> todos
  ToggleTodo ix          -> update (Just . toggleTodo) ix todos
  StartEditTodo ix       -> update (Just . startEdit) ix todos
  FinishEditTodo (v, ix) -> update (Just . finishEdit v) ix todos
  DeleteTodo ix          -> delete ix todos
```

Though it’s not necessary to define this function, we are using several other auxiliary functions here, which we’ll need further, anyway.

```Haskell
startEdit :: Todo -> Todo
startEdit todo = todo { todoState = TodoActive True }

finishEdit :: Text -> Todo -> Todo
finishEdit val todo = todo 
  { todoState = TodoActive False, todoText = val }

toggleTodo :: Todo -> Todo
toggleTodo Todo{..} = Todo {todoState = toggleState todoState,..}
  where
    toggleState = \case
      TodoDone     -> TodoActive False
      TodoActive _ -> TodoDone

nextKey :: IntMap Todo -> Int
nextKey = maybe 0 (succ . fst . fst) . maxViewWithKey
```

The function adding a new element has also changed and now returns the event, not the task itself. Let’s also add the field cleanup after the new task is added.

```Haskell
newTodoForm :: MonadWidget t m => m (Event t (Endo Todos))
newTodoForm = rowWrapper $ el "form" $ divClass "input-group" $ mdo
  iEl <- inputElement $ def
    & initialAttributes .~
      (  "type" =: "text"
      <> "class" =: "form-control"
      <> "placeholder" =: "Todo" )
    & inputElementConfig_setValue .~ ("" <$ btnEv)
  let
    addNewTodo = \todo -> Endo $ \todos ->
      insert (nextKey todos) (newTodo todo) todos
    newTodoDyn = addNewTodo <$> value iEl
    btnAttr = "class" =: "btn btn-outline-secondary"
      <> "type" =: "button"
  (btnEl, _) <- divClass "input-group-append" $
    elAttr' "button" btnAttr $ text "Add new entry"
  let btnEv = domEvent Click btnEl
  pure $ tagPromptlyDyn newTodoDyn $ domEvent Click btnEl
```

Function `todoListWidget` now returns the task change. It has slightly changed, too:

```Haskell
todoListWidget :: MonadWidget t m => Dynamic t Todos -> m (Event t (Endo Todos))
todoListWidget todosDyn = rowWrapper $ do
  evs <- listWithKey 
    (M.fromAscList . IM.toAscList <$> todosDyn) todoWidget
  pure $ switchDyn $ leftmost . M.elems <$> evs
```

The first thing we notice is that the `simpleList` function is replaced with `listWithKey`. They differ from each other in the type of the first parameter – the first function accepts the list `[]`, the second one accepts `Map`. The list will be sorted by the key. Here the returned value is important. Each task returns an event (deletion, change). In our specific case, the `listWithKey` function will look as follows:
```Haskell
listWithKey
  :: MonadWidget t m
  => Dynamic t (Map Int Todo)
  -> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
  -> m (Dynamic t (Map Int TodoEvent))
```
> Note: this function is a part of the `reflex` package and has a more complex type. Here we show the specialized type.

Here we are using the familiar `leftmost` function for all `Map` values. The expression `leftmost . elems <$> evs` is of the following type: `Dynamic t (Event t TodoEvent)`. We use function `switchDyn` to retrieve the `Event` from the `Dynamic`. This function operates in the following way: it returns the event occurring when the internal event takes place. If the `Dynamic` and `Event` occur simultaneously, the previous `Event` will be returned until the event in the `Dynamic` is updated. The function `switchPromtlyDyn` operates differently: if the `Dynamic` update, occurrence of the event existing before the `Dynamic` update, and triggering of the event now containing the `Dynamic` take place simultaneously, the new event containing the `Dynamic` will be returned. If this situation is not possible, it’s always better to use the `switchDyn` because the `switchPromtlyDyn` function is more complex, performs additional operations and, moreover, can create cycles.

The task has acquired different states, which is why the function representing one task has also changed:

```Haskell
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
  todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
    TodoDone         -> todoDone ix todoText
    TodoActive False -> todoActive ix todoText
    TodoActive True  -> todoEditable ix todoText
  switchHold never todoEvEv
```

Here we’re using the new function `dyn`. It has the following type:
```Haskell
dyn
  :: (Adjustable t m, NotReady t m, PostBuild t m)
  => Dynamic t (m a)
  -> m (Event t a)
```
It accepts the widget wrapped in the `Dynamic` as the input parameter. This means that each update of the `Dynamic` will be followed by the `DOM` update. The output value is the event carrying the value the widget returns. In our case, the specialized type will look as follows:

```Haskell
dyn
  :: MonadWidget t m
  => Dynamic t (m (Event t (Endo Todos)))
  -> m (Event t (Event t (Endo Todos)))
```

Here we come across an event nested into another event. Two functions from the `reflex` package can perform operations on such type: `coincidence` and `switchHold`. The first function returns the event occurring only when the external and internal events occur simultaneously. That’s not our case. Function `switchHold` is of the following type:
```Haskell
switchHold :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t)
```
This function switches to the new event each time the external event occurs. The event passed as the first parameter will hold until the external event occurs for the first time. This is exactly the way we’re using this function in our example. No event can come from the list until the list is changed for the first time, so we use the `never` event. As the name implies, this is a special event that never occurs.

Function `todoWidget` uses different widgets for different states.

```Haskell
todoActive :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoActive ix todoText = divClass "d-flex border-bottom" $ do
  divClass "p-2 flex-grow-1 my-auto" $
    text todoText
  divClass "p-2 btn-group" $ do
    (doneEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Done"
    (editEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Edit"
    (delEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Drop"
    pure $ Endo <$> leftmost
      [ update (Just . toggleTodo) ix <$ domEvent Click doneEl
      , update (Just . startEdit) ix  <$ domEvent Click editEl
      , delete ix <$ domEvent Click delEl
      ]

todoDone :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoDone ix todoText = divClass "d-flex border-bottom" $ do
  divClass "p-2 flex-grow-1 my-auto" $
    el "del" $ text todoText
  divClass "p-2 btn-group" $ do
    (doneEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Undo"
    (delEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Drop"
    pure $ Endo <$> leftmost
      [ update (Just . toggleTodo) ix <$ domEvent Click doneEl
      , delete ix <$ domEvent Click delEl
      ]

todoEditable :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoEditable ix todoText = divClass "d-flex border-bottom" $ do
  updTodoDyn <- divClass "p-2 flex-grow-1 my-auto" $
    editTodoForm todoText
  divClass "p-2 btn-group" $ do
    (doneEl, _) <- elAttr' "button"
      (  "class" =: "btn btn-outline-secondary"
      <> "type" =: "button" ) $ text "Finish edit"
    let updTodos = \todo -> Endo $ update (Just . finishEdit todo) ix
    pure $
      tagPromptlyDyn (updTodos <$> updTodoDyn) (domEvent Click doneEl)

editTodoForm :: MonadWidget t m => Text -> m (Dynamic t Text)
editTodoForm todo = do
  editIEl <- inputElement $ def
    & initialAttributes .~
      (  "type" =: "text"
      <> "class" =: "form-control"
      <> "placeholder" =: "Todo")
    & inputElementConfig_initialValue .~ todo
  pure $ value editIEl
```

All functions used here were described before, so we won’t go into detail explaining each individual function.

## Optimization

Let’s get back to the `listWithKey` function:
```Haskell
listWithKey
  :: MonadWidget t m
  => Dynamic t (Map Int Todo)
  -> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
  -> m (Dynamic t (Map Int TodoEvent))
```
The function operates in such a way that any update of the transmitted `Dynamic` will initiate the update of each individual element. Even if we, say, change a single element, the update will be passed to each element, though it won’t change the value. Now let’s get back to the `todoWidget` function.
```Haskell
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
  todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
    TodoDone         -> todoDone ix todoText
    TodoActive False -> todoActive ix todoText
    TodoActive True  -> todoEditable ix todoText
  switchHold never todoEvEv
```
As you remember, the `dyn` function updates the `DOM` every time the `todoDyn` is updated. Considering that the change in one element of the list is passed to each element individually, it turns out that the entire `DOM` section which displays our tasks will be rebuilt (you can check this using the developer panel in the browser). Obviously, this is not what we want. This is when the `holdUniqDyn` function comes to the rescue.

![](https://blog.typeable.io/images/reflex/2_2.gif)

```Haskell
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t TodoEvent)
todoWidget ix todoDyn' = do
  todoDyn <- holdUniqDyn todoDyn'
  todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
    TodoDone         -> todoDone ix td
    TodoActive False -> todoActive ix td
    TodoActive True  -> todoEditable ix td
  switchHold never todoEvEv
```

We’ve added the line `todoDyn <- holdUniqDyn todoDyn'`. What’s going on here? The matter is that though the `Dynamic` operates, the value it contains remains unchanged. Function `holdUniqDyn` works just this way, so that if the `Dynamic` passed to it operates and hasn’t changed its value, the output `Dynamic` won’t operate and, consequently, in our case, the `DOM` won’t be rebuilt unnecessarily. 

![](https://blog.typeable.io/images/reflex/2_3.gif)

The result we obtained can be viewed [in our repository](https://github.com/typeable/blog-posts-ru/tree/c6f256e96d24507ea3e3040c3cd490aaec610029/reflex-todo).

In the next part, we’ll consider another way of managing the events and the use of the [GHCJS-DOM](https://hackage.haskell.org/package/ghcjs-dom) library.