Halogen vs Concur sample app

I have just pushed a simple repository with a full application that runs two copies of a trivial calculator in the browser page:

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?

The skelton of the application was started during the MeetLambda meetings, together with the other attendees: https://github.com/MeetLambda https://twitter.com/meetlambda

5 Likes

In the concur examples they put the <script> tag before the closing </body> tag. Does that help?

It’s another option to solve the problem, but I don’t like it very much.

@gcsolaroli could you compare and contrast a bit about the two libraries? Then, what is your personal preference given this small app?

Also, pinging @ajnsit to see if he has any ideas about your onLoad issue.

3 Likes

Thanks for the ping @dstcruz!
@gcsolaroli Thanks for doing this! it’s always nice to see a comparison with other UI libs.

I see that Halogen awaitBody waits for the document.onReady event. I have now added that to Concur, so your example works correctly without any hacks. It’s this commit - https://github.com/purescript-concur/purescript-concur-react/commit/945320e30f0feb4a3f61382b565383e2080927d5. Not released yet, but I will get around to it very soon.

Also, FYI, I already had a (postfix) calculator example in concur - https://github.com/purescript-concur/purescript-concur-react/blob/master/examples/src/Test/Calc.purs. It’s pretty similar to yours but all in one file.

2 Likes

Thanks @ajnsit!

I will look into your change, hoping I will be able to use a ‘not released’ library.

I looked into your example, and also modified it a little bit: https://github.com/gcsolaroli/ConcurCalculator.

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. :slight_smile:

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

Hope this makes some sense. :slight_smile:

1 Like

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 -

  1. 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
  1. 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 {}

Inversely you can import existing React components into Concur using the guide on the Concur github page - https://github.com/purescript-concur/purescript-concur-react#external-react-components

3 Likes

@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. :slight_smile:

@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.

1 Like

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. :sob:
But I will share what I learn in the process. :slight_smile:

2 Likes

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.

1 Like

@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.

Does this make any sense?

1 Like

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 :slight_smile:

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.

1 Like

I think Concur leverages Control.Alt in order to handle combination of events, and does not have its own specific code.

Again, take my suggestions with care, as I am still learning myself.

Here you can find a very simple, but still complete, example on how to use keyboard events with Concur:

I wrote it using the Concur own example as a starting point, but it may help to see a different take on the same issue to understand it better.

2 Likes