Understanding Free Monad Queries¶
Most of the time Halogen queries look like this:
1 2 3 4 | data QueryF (… other type arguments omitted …) a = ... | SetVisibility Visibility a | GetVisibility (Visibility -> a) |
(where QueryF is used directly as the Halogen query functor)
This library takes a slightly different approach: the query functor is actually Control.Monad.Free.Free QueryF, the free monad generated by the query functor.
This allows queries the full power of monadic (and applicative) composition: sequencing effects, determining control flow based on previous results, and my favorite: doing nothing (pure unit).
We now define smart query constructors for this Free pattern like so:
1 2 3 4 5 6 7 8 | -- | Set the container visibility (`On` or `Off`). setVisibility :: ∀ o item eff. Visibility -> Query o item eff Unit setVisibility v = liftF (SetVisibility v unit) -- | Get the container visibility (`On` or `Off`). Most useful when sequenced -- | with other actions. getVisibility :: ∀ o item eff. Query o item eff Visibility getVisibility = liftF (GetVisibility id) |
Different patterns¶
In the simple cases, the helpers Halogen use to work with raw query constructors are folded into the smart Free query constructors already: H.action (SetVisibility On) becomes simply setVisiblity On, and similarly H.request GetVisibility is just getVisibility. This is because these patterns are typically present already smart constructors: setVisibility returns Free QueryF Unit, since it is an action, and getVisibility returns Free QueryF Visibility, since it requests the visibility. This allows for easy composition in do notation:
1 2 3 | toggleVisibility = do vis <- getVisibility setVisibility (not vis) |
C’est très facile!
Event handlers look a little different. This is one example:
1 2 3 4 | HE.onMouseDown \ev -> Just do Select.preventClick ev Select.select index when doBlur Select.triggerBlur |
(Of course you may return Nothing if you so wish, but its effect is just like pure unit now.)
If you do not need access to the argument ev, Select.always provides a simple shortcut for const <<< Just:
1 | HE.onMouseOver $ Select.always $ Select.highlight (Index index) |
Returning non-unit values¶
Use map or <$ or pure to return other types of values from a query. So, instead of something like this:
1 2 | H.subscribe $ eventSource' someEventSource \value -> Just (SetVisibility value H.Listening) |
Use
1 2 | H.subscribe $ eventSource' someEventSource \value -> Just $ setVisibility value $> H.Listening |
or
1 2 3 4 | H.subscribe $ eventSource' someEventSource \value -> Just do setVisibility value pure H.Listening |
Many thanks to Nicholas Scheel for providing the implementation of QueryF and the documentation above.