The only thing I don’t like about Halogen, is the fact that there are too many arrays when bulding html and sometimes it gets a little hard to keep track of the parentheses.
The other problem is that it’s a bit unpleasant to handle optional html elements.
Both these problems can be fixed ofc by splitting code in smaller functions, but I was thinking of something different.
Is it possible to use MonadWriter with the current implementation of Halogen? I’m asking before starting to try as maybe there are issues that will make me waste hours trying to do something that’s impossible
My idea would be to have something like this (also using a record for props):
runHtml $ do
HH.div { classes: [ columns, isDesktop ] } $ do
HH.div { class_: column } $ do
HH.h1 { class_: title } $ HH.text "TITLE"
HH.button { disabled: loading
, onClick: HE.input_ Click
} $ HH.text "CLICK ME"
when foo $ do
HH.p_ $ HH.text "SURPRISE"
It’s definitely possible to do alternative things with the HTML syntax, but anything other than what we have will have a performance penalty. It’s unlikely we’ll support it in Halogen itself, but it could always be a library.
For what it’s worth, I think the theoretical example is no better at all than the bottom personally , and the bottom one can be much improved by alternative formatting:
So far I have been using an operator with type Array a -> Array (Tuple a Boolean) -> Array to handle optional elements, but , while it’s decent for props, can get very annoying for html subsections.
Your solution is much cleaner, but it suffers from the same problem: the need to change big part of the structure to handle a single optional element.
Adding the extra indentation is surely helpful, I wonder if I can change some setting so that my editor uses that by default.
Any sort of Monadic syntax is going to either be wildly stack-unsafe or extremely inefficient. You can’t use naive Writer because you will very quickly blow the stack, so lets say you trampoline it through Free. You’ll end up at least paying for 1) accumulating the Monoid 2) the spine of Free 3) All the heap allocated closures incurred from Monadic binds. This just gets very expensive for anything non-trivial.
For optional elements I’d define:
when :: Boolean -> (Unit -> HTML ...) -> HTML ...
when b k = if b then k unit else H.text ""
It’s true that this inserts a dummy text node rather than nothing, but in many cases this ends up being more efficient in the long run. If you are not using keyed elements and you are toggling an element that isn’t at the end of the list, you can end up paying to destroy and rerender subsequent siblings. But if you use an empty text node, then the DOM position of nodes stays stable and they can be diffed normally.
FWIW, I hide single optional elements, error messages in my case, by setting their CSS display property to none when they should be hidden. They are always present in the DOM, but it’s a relatively simple solution and it works.
Nice to know!
Why are you passing Unit -> HTML instead of just HTML? Is it to postpone evaluation of something until the component needs to be rendered?