The interesting part is that one instance is implemented with Halogen (0.5) and the other using Concur. Both share the same “model” that is also unit tested.
The application is trivial, but complete; it uses just spago, parcel and yarn; there is a yarn script to serve the application (with auto-reloading of the page on changes), and also scss are automatically processed.
Hope this could be useful to someone.
There is a (very) minor issue I would have liked to handle differently, but could not solved myself: I had to defer the start of the application after the ‘onload’ event in the DOM window, otherwise Concur could not find the node where to bind its widget.
Does anyone know how to achieve something in the line of Halogen.awaitLoad for Concur?
But, we had already started writing our own calculator using Halogen, embedding the logic into the component, in a way similar to what you have done in your example.
But then we had some issues, and in order to examine them faster, we refactored the code in order to be able to have simple tests to help us.
At that point, as the logic was all packed together, trying two different UI library was almost trivial, and pretty interesting too, as it allows to better see the different approaches used.
I am VERY newby with PureScript, and even more with Halogen, so you should take my view very carefully.
From what I have see so far, Concur is WAY simpler to use and get started. I may probably be biased, having looked into Concur only after having spent a lot of nights trying to wrap my head around Halogen; the process was tedious, but I also learned a LOT about PureScript that I was (and still am) not fully understand.
There is a key element to consider though: Halogen is meant to be able to wrap any off the shelf Javascript component, and made it usable inside a PureScript application. Concur does not try to solve this problem, and in exchange provides you a much simpler API.
I think I will keep on trying to use both libraries together (like in the calculator example) also for the next sample application, as this provides many benefits:
keep the “business” code isolated from the UI
allow to compare two UI approach side by side
verify performance differences between the two libraries
That’s great! I would love to see more such examples comparing Concur with Halogen.
W.r.t. external JS components, Can you elaborate if there was something that was hard to get working with Concur? I think Concur allows JS interop fairly easily -
It’s probably underappreciated how much the Concur time monad helps to provide a natural and principled place to insert JS side effects. For example, if you want to print something to the console every time a button is clicked, you can simply insert it in the flow (I’m not sure how Halogen handles this) -
do
_ <- button [onClick] [text "Click me"]
liftEffect $ log "Button clicked"
-- .. Do more things here
Concur makes it extremely easy to interop with components “native” to its backend, which right now means React. For example you can use Concur.React.componentClass to turn any Concur widget into a React class, which can then be used with other React components.
componentClass :: forall a. Widget HTML a -> R.ReactClass {}
@ajnsit, as far as I understand Halogen, its machinery was set up in order to wrap not just regular React components, but full stateful “applet” like CKEditor.
But my understanding is still very shallow.
@gcsolaroli given what you’ve said about learning Halogen, you may be interested in refactoring the Halogen side to use Halogen Hooks: Introducing Halogen Hooks
I’d be interested to know how you think the two compare if you decide to try that.
Halogen Hooks (and the rewrite of Formless) are on my todo list; unfortunately, the more I dig any subject, the more material I find to study.
At the moment the pile of topics/material to study understand grows much faster than what I am able to digest and learn.
But I will share what I learn in the process.
Okay so Concur has piqued my interest. Has anyone had a dig around in the source code?
Trying to understand, when composing widgets in time, what is the mechanism by which we block rendering?
For example when we have a button with, say, an onClick event which must be handled in place, before the next widget in the do block renders. Seems to be the Discharge module, but am finding some of it tough going.
@ajbarber I am not sure I understand your concerns right; but as far as I have understood Concur (and I am still a newbie), you don’t block the rendering of the component halfway.
You fully render the component, and then the flow of execution halts until the component does emit one of it values; at that point you handle the new value and decide how/what to render again.
Thanks @gcsolaroli. Before committing to using I like to have a little dig around in the actual purescript-concur-core source code to understand some of the concepts more deeply. I am one of those that needs to open up the bonnet before test driving
What I am particularly interested is how the flow of execution halts as you put it, how this is actually implemented in the original concur source. It was not immediately obvious to me scanning the purescript-concur-react/purescript-concur-core source code.
I’m presuming there has to be some logic which checks for a return type of SyntheticMouseEvent or some other synthetic DOM event on the composed widget, which then blocks further widgets from rendering. But some of the purescript-concur source code is rather opaque to me.