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.