@thomashoneyman was kind enough to give me long answer to a question I had. Posting here with his permission. Thomas said he might clean this up a bit. So you might want to wait for his update.
Question: I am reading some tantalizing things about Halogen, that it is full of deep functional ideas. This could mean one of two things:
The comment is from the perspective of JS programmers, and Halogen alternatives based on React have too much of an imperative feel and Halogen is merely more idiomatic in comparison
Halogen has advanced FP ideas even relative to typical idiomatic Haskell. If it is the latter, can you please list two or three FP ideas in Halogen?
I think your first option is the closest to the truth. Halogen has a similar philosophy to React with regards to building a user interface out of components which encapsulate local state. Halogen stays faithful to PureScript ideas when implementing the philosophy, hence the note about Halogen expressing “advanced” functional ideas. PureScript bindings to React, however, contort themselves somewhat to adapt to React — it’s ultimately a JavaScript library with JavaScript ideas.
As one example, Halogen supports effect polymorphism — in other words, you can run your components in whatever monad you would like, providing whatever effects you would like. In PureScript this is mostly called the “capability pattern” for structuring applications. You aren’t restricted to Effect
. But when you use the low-level React bindings you can only use Effect
for your components. And when you use React Basic you can use effect polymorphism, but only because of plenty of boxing and indirection that the library introduces for you under the hood. In Halogen there’s almost zero extra work to make this possible. It’s just idiomatic PureScript.
As another example: in Halogen, you can prevent renders by only updating state if the old state isn’t equal to the new state, or you can memoize data if it’s unchanged between renders, and so on. But React, being a JavaScript library, uses reference equality all over the place. That means that when you use a React feature for memoizing data, for example, you have to be really careful: it’s no longer a simple equality test like normal Halogen or normal PureScript code. Instead, you have to ensure that you return the same reference to the underlying data if it really is unchanged. The same goes for components: you have to be careful not to return a component from a function, because its reference can change, at which point React will think it’s a brand new component.
React will consider these to be three different arrays if you go to memoize them:
a = [ 1, 2, 3 ]
b = [ 1, 2, 3 ]
c = map (\x -> x) b
…but Halogen will recognize these arrays are all equal because its memoization functions use PureScript’s notion of equality rather than reference equality.
These examples aren’t really anything to do with advanced functional programming ideas. They instead are normal ways that PureScript programmers think about code. However, they do show how Halogen naturally does everything the way you would write a normal PureScript program in the functional style.
But Halogen does also introduce some interesting ideas. One of the most obvious is that the entire evaluation model is a free monad, and free monads are a fairly famous, somewhat intimidating functional programming idea. Programming using free monads is simply different from the typical imperative way of writing programs in something like React.
This has become a much lengthier explanation than I intended at first, so I’ll stop here But if it’s helpful, I started using Halogen with very little functional programming knowledge. I don’t think the framework requires much advanced knowledge of functional programming. But it certainly lets you think in functional concepts when building your user interfaces and has a high ceiling on how far you can push them.