Hacker News new | past | comments | ask | show | jobs | submit login
Client-side web programming in Haskell: A retrospective (chrisdone.com)
163 points by mxschumacher 29 days ago | hide | past | web | favorite | 47 comments

There's one very important thing that people miss when they mention Elm vs PureScript (or others) which bothers me.

Here's my message: Elm is playing the JS world's game (obeying the rules) and it's winning it!

- you can take a JS dev and they'll be productive in two weeks in Elm

- the compiled code will be smaller than react's

- compilation time is less than in webpack (and compiler's reaction is under 0.2 secs most of the time)

- performance of your app is better

- no runtime exceptions

PureScript and others, unfortunately (at least in many cases), take a different direction: they don't play the game at all, staying in their own, very different one. But hey, they have type-classes!

> Elm is playing the JS world's game (obeying the rules)

I disagree, though the meaning isn't clear. Elm is interesting, but it's not part of the JS world, and it doesn't come close.

Just recently, the Norway Railways described their experience with Elm. It was largely positive, but one of the main two negative points was the difficulty to interoperate with JS libraries. They tried it, and failed hard (many runtime errors). BTW, the other pain point was the restrictions on packaging, which is quite orthogonal with the (npm) JS world. https://news.ycombinator.com/item?id=21204761

By "playing by the rules" I meant that Elm doesn't give up the big considerations that lie outside of the purely linguistic features, unlike other ML-family JS languages.

I don't claim Elm to be a part of JS world, and your points about JS interop are correct, it's somewhat difficult if it's heavy, so using Elm as a driving for some big JS library would be a bad idea, while using some small interface for drawing charts or WYSIWYG is ok.

The private packages point is specific to how they do stuff, but I agree that it would be an improvement to allow that from package.json.

> unlike other ML-family languages

I'm curious about this comment, for my current project I chose reasonML over Elm very much because reasonML plays well with old javascript and it plays nice with the rest of the web.

My bundle is very small, compile times fast, and interop with even quite old JQuery libraries is a breeze.

Is there something else I'm giving up that I'm not aware of?

Elm doesn't allow you to call JavaScript code in Elm anymore. The reasoning is to prevent runtime exceptions. Instead, you have to go through something called ports, which is like passing JSON around. That's why there are people who complain about the Elm-JavaScript interop.

Right, when I was first exploring alternatives to Javascript was around the time Elm was removing first class FFI's in favor of ports and it was the primary reason I chose not to use it.

But the poster I responded to was saying that Elm made considerations about the wider web that none of the ML family languages had and I was hoping to find out what those might be since I'm not aware of any non-language advantages of Elm over ReasonML.

What doesn't 'playing by the rules' even mean? It feels like a highly subjective and disingenuous comment. Elm's focus on SPAs is fine and good, but very narrow compared to what JavaScript can do. Elm's port system, it's way to interop with JavaScript, can't do synchronous code which can be a no-go. All of your bullet points apply the same reasons people say to use ReasonML. PureScript offers the same safety if you 'play' by its rules.

I do Elm at my job every day, but it has serious competition.

By "playing by the rules" I meant that Elm doesn't give up the big considerations that lie outside of the purely linguistic features, I've listed some of them.

PureScript is a tremendous language, and I'd be happy to see it more widely used, and your points about JS interop and SPA focus are true of course, but I would argue that the step for ditching FFIs are better for some of the same reasons that were listed.

I think you might be missing part of the philosophy. If you take a look at purescript-web-dom, most of the browser APIs have merely been wrapped. This saves you work and you can use it if needed, but it is encourage to use this as a foundation to build better APIs/DSLs (heck, steal them from Elm). Elm does a similar thing under the covers, and they release an often better API than the browsers, however, you can't use this intermediate representation to build alternative APIs and many times you have to wait years for anything to even pop-up in elm-explorations because nothing is satisfactory if it doesn't improve upon the browser's standard. This philosophy has its pros and cons, and while sometimes the APIs can be clunky in PureScript because it's just wrapping the browser API, I never felt straight-up blocked by a missing feature.

With Elm you can't, for example, use the browsers Intl API to get plurals, currencies, dates, etc. This is the type of thing you want to partially apply/instantiate with your locale, then use it synchronously at the view level with your model for display. You just don't have the access to do this because a proposal must be made and approved by the Elm team. It'll probably be good API, but you'll be SOL in the meantime (for possibly years). This isn't a dev cycle that works for every person, team, or project.

This is so spot on. Asynchronous ports are troublesome in a lot of cases. That's why I don't usually do my apps in Elm.

You're mixing your "playing by JS' rules" point with just general Elm features. Faster compilation speed, faster executables, and smaller executables are not JS' rules, they're just generally desirable things to have. "Faster than Webpack" and "no runtime exceptions" are certainly not the JS world's rules (does Purescript have runtime exceptions?). That a typical JS developer can get up to speed quicker in Elm seems very nebulous to me; why? Could you elaborate more on what you're talking about? How is Purescript not playing by JS rules, other than being (I assume) slower than Elm?

> you can take a JS dev and they'll be productive in two weeks in Elm

i’ve never been a big fan of these sorts of assertions. not everyone picks things up at the same rate.

I wonder of what of those points would be impact had typeclasses been implemented into ELm.

It would be a bit of a rabbit hole, as many common uses of typeclasses in Haskell require HKT, which Elm doesn't have. Typeclasses per se could be bolted on to Elm relatively easily, but I suspect that adding HKT without compromising Elm's other advantages (e.g. fast compiles, good error messages) would be quite tricky.

That's a good question! Some things are hard to predict (compile times), some are easier (their abuse would grow, very soon Elm would loose the first "newbie friendliness" point).

Yeah, I think typeclass by themselves are not harmful, but then common libraries will be full of them, providing nice error messages would be harder, and then you might attract too much of the category theory folks declaring functors everywhere and scaring the newbies. (Nothing against category theory, but it's not really newbie friendly which is what Elm tries to be)

Interestingly enough, I think that Category Theory is more of a saviour here than anything, reason being this: most of the harm from over-using type classes comes from the cases when their usage can be avoided, and a general rule of thumb in the Haskell world is now this: if there are no laws that your type-class is bringing, avoid using it. Thus, Functor is ok, but "HasStatsField" stuff is not really.

HasField typeclass was recently added to GHC. It has no laws.


Luckily, all that stuff will be outdated with somewhat proper records https://github.com/ghc-proposals/ghc-proposals/pull/282

The RecordDotSyntax extension is just syntactic sugar for the HasField typeclass. It doesn't make HasField "outdated". Anyway, the HasField typeclass (with the proposed change to support field updates[1]) does have at least one law:

    uncurry ($) (hasField @x r) == r
or expressed with the getField and setField wrappers:

    setField @x r (getField @x r) == r
[1] https://github.com/ghc-proposals/ghc-proposals/blob/master/p...

This post dismisses Elm on the basis of lack of typeclasses, but fails to mention miso (https://haskell-miso.org), that includes typeclasses. Miso is Elm-as-an-embedded-DSL written as a Haskell library for GHCJS that allows one to share types on the front and backend, take advantage of the entire Haskell ecosystem and has experienced commercial use.

I agree that Miso deserves a mention. It seems to be very actively maintained.

On the other hand, if you reject GHCJS then Miso is out of the running immediately. And the reason to reject GHCJS (big bundles, as mention in the article) is something you dont have in Elm/PureScript/ReasonML.


There is a company that has a 400 module miso application and the bundle size is not an issue (https://www.polimorphic.com). After closure compilation GZIP'ing, pre-rendering and caching of the JS, it's a one-time cost, and is negligible due to pre-rendering. We build websites for users, who care only about experience, not about how large the payload is. Payload only matters insomuch as it adversely affects user experience. This argument is strictly a developer concern and is in no way correlated with end-user feedback. There are many <1MB js payload size websites with 0 users.

After our bad experience in 2016 using GHCJS on a big project, we haven't considered it for anything since. Anything that arrived after that related to GHCJS just hasn't been on my radar ("a history of my own experience in this problem space").

Would you be able to share more details as to what caused this bad experience? My experience with GHCJS on a big project has been the opposite.

Note that Elm has typeclasses, they are just not exposed in the language: https://medium.com/@terezk_a/haskell-in-elm-terms-type-class...

Considering that Elm seems to be by far the most stable, usable and used alternative, just dismissing it for the lack of typeclasses seems a bit short sighted to me. Is this a case of avoiding success at all costs? :)

Elm can be looked at like Haskell on training wheels, in a good way, and that simplicity very likely contributed to its popularity.

We already tried Fay as a team (15kloc codebase to write an IDE), which was literally Haskell without type-classes. We've learned that we want type-classes, YMMV.

There are some ways in which Haskell's type system actually makes it significantly better at Elm's core job than Elm itself.

Type classes could certainly be argued to be one of those things, but also just existential types makes "reducers" a lot more pleasant to work with.

Check out e.g. Edward Kmett's library or read e.g. Gabriel Gonzales' post on folds if you want to know more.

[1] http://hackage.haskell.org/package/folds-0.7.5/docs/Data-Fol...

[2] http://www.haskellforall.com/2013/08/composable-streaming-fo...

Yeah, but I’d love to see the average JavaScript developer get up and running with Haskell in just two weeks.

Elm confronted a dizzying amount of abstraction and category theory prevalent in Haskell implementations of FRP and over time converged on a solution so amusingly simple that it ended up dropping the FRP branding entirely because it just seemed silly. Elm absolutely succeeded and gained real-world traction because it took what was at that point an overly academic and unsuccessful idea and aggressively pursued simplification. Perhaps one could argue that this pursuit had a cost as well -- e.g., the lack of typeclasses -- but I just don't think Elm would exist without that driving mission.

Is Elm still dependent on one maintainer[0]?


Yes. Until Evan stops gatekeeping which packages are allowed to be published, Elm is a high-risk experimental language best suited to experimental projects.

>considering that Elm seems to be by far the most stable, usable and used alternative

I wonder, does Elm not having type classes is actually a feature that boosted its success, or is it just missing because of the author stubbornness ?

I think Elm did right into focusing in SPA rather than being yet another language compiling to JS.

> does Elm not having type classes is actually a feature that boosted its success

I dont think so. It did help make errors very straight forward and prolly also keeps compile times low.

The author's stubbornness is, in my view, more in that he does not support (actually prevents where he can) making Elm usable outside of the browser (like what Node did to JS).

Seems like it. There are some tasks in Elm which would benefit from typeclasses but in my experience they're not so common to be much of a concern. Sure, I'd like typeclasses but I'm not dropping down to JS just because of their absence.

Of note v5-rc of PureScript's Halogen helps with some of the complications. It's been in RC for several months waiting for all of the documentation to wrap up, but I used it on a small project a few months ago and didn't have too many problems outside trying to understand how to wire up requestAnimationFrame to emit events on init. It's worth another look once it's released.

Some documentation on the v5 changes: https://github.com/slamdata/purescript-halogen/blob/master/d...

V5 of Halogen is the nicest frontend framework I have ever used. It is a bit frustrating that the docs are taking so long to come out so it can be released properly, but in terms of using it the framework is good to go and in my opinion well worth the effort to learn how to use it.

A great resource for learning about Halogen that has been updated to V5 is Thomas Honeymans Real World project : https://github.com/thomashoneyman/purescript-halogen-realwor...

I'm working on a few tutorials for V5 too, but finding the time to be prolific is hard : https://codersteve.dev/tags/halogen/

I actually PR's these docs a couple weeks back. In short halogen v5 greatly simplifies a lot of the complexity around parent/child components.

Did you ever look at ScalaJS? I found it surprisingly pleasant and reliable, and while Scala is not Haskell, it is a mature language with typeclasses and probably the best tooling support (in terms of IDEs etc.) of anything in this space.

Maybe I'm missing the point here but I couldn't see where this article mentions what they were trying to do in a web client.

Surely that should be the first variable to consider?

Fat clients of all kinds (cryptocurrency wallets, medical devices, data analysis, log viewers, etc.), depends on the client.

While there was a novelty budget consideration, I miss comparison for:

* ease of development

* time to learn the language

* documentation: tutorials, etc

* ease of hiring

Architecture is a tiny cost you have to pay for using something else.

Then again, for a consultancy using Haskell, PureScript is the right choice.

> Our meeting notes are long and detailed, the brief summary: we don’t feel comfortable with GHCJS, Reflex is great but also costly (double novelty budget), Halogen is a better architecture than Elm. PureScript and Halogen come out as the best possible choice. Other things considered: ClojureScript, TypeScript, Rust, ReasonML, Elm, OCaml.

I would love to read these notes!

A summary at the top and maybe a table of all the options would be useful additions here.

Haskell in web rocks!

It does seem rocky when reading that retrospective.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact