This is a fantastic post and I really appreciate your rationale (having faced similar struggles). A limited but simplified set of Haskell libraries that offers similar ergonomic benefits to Elm is very appealing! It would be great if this became a community project.
I'm not sure if the poster is from NoRedInk, but my only feedback is that I'd love to see some sample code that demonstrates the power of the handle pattern in use?
I followed the link that discussed the pattern which then referenced this https://github.com/jaspervdj/fugacious. It would be better tho if there was a concise sample.
- I’ve used everything. NodeJS, React, Rails, Python , Rust. Once I learned Haskell I fell in love and never went back. I code in Haskell because I enjoy it, I can sleep at night because of the safety guarantees (no crashes), and I find it aesthetically pleasing and easy to think in. One of the things I enjoy most is the stability of the package APIs.
- I try to avoid JS. I do progressive enhancement with vanilla JS, and avoid JS webapps unless absolutely necessary. If I need to write a JS webapp, I use a purely functional approach. I found that when writing React, using frameworks, using TypeScript or PureScript, I lost so much time and effort managing my build step and fighting with APIs and package churn. I use preact with the htm library, vanilla JS, and most importantly no build step and no npm. I don’t use a framework. I use a purely functional approach inspired from Haskell/Elm. I have an update function which returns a new state and effects from the current state and event, and a dispatch function which is attached to onevent props. The dispatch function calls the update function, renders, and runs effects. These few functions are 20 lines of code.
- No challenges specific to Haskell so far. Of course I had to learn the ecosystem when I started, which packages to use, language and tooling gotchas, etc. but that is the case for any language.
The most important thing for me is rapid development with no surprises and package churn, since I'm either trying to get an MVP out ASAP, or I'm trying to deliver under a deadline for a client.
The two functions, render and dispatch, are essentially the only code I share across the projects. That's the whole "framework". It's 19 lines of code. All it requires is a link to Preact, which is 14K. Oh, and I just keep everything in one big file. Simple, but I've built multiple complex single-page JS apps with this approach.
I'm sure someone will tell me I'm doing it all wrong, and I need to use Next.js with webpack, a giant stack of npm packages, code-splitting and bundling, and whatever craziness front-end developers are doing today.
Thanks for the example. You're right about package churn, especially if you're working alone. You don't want to spend all your time updating dependencies. Not having a build step must be a breath of fresh air too.
On second thought, there is one minor annoyance with Haskell: Most companies don’t release an API client for it, so I often have to make the HTTP calls myself to various third party services.
I tried writing a bunch of F# once before, but one major issue that came up was working with JSON. There's a lot of good libraries for serializing/deserializing, but sometimes you need to do something like injecting a value into a an object, and that's just not easy or type safe to do. How hard is working with JSON in Haskell? Any particular libraries/extensions that make it easy?
Another question that came to mind: how good is the ecosystem for Haskell? Eg, if you wanted to deal with, say, generating PDFs, would you be more or less confident that you could get something working over a weekend?
E: Completely forgot to mention, sumi.news looks great!
The thing is, I needed a very specific kind of output for the JSON request I needed to make, and from messing around with lenses I couldn't get it work out. I don't remember what the exact usecase was, but for a hypothetical example, consider an awful POST endpoint that needs the authorization token to be in the json body. So you would have a request like:
{
"page": 1,
"token": "eyJ..."
}
But ideally, you'd abstract out the auth stuff and put it in a central location which injects it into all requests that need it, eg. if you're using axios you'd add an interceptor for it. We couldn't find a way to do this without making the request into a Dictionary<string, obj>. It's entirely possible this could work with lenses, but from what I looked at I couldn't make it happen.
As an aside, I chuckled at the 'Did they fail you, too?'; it made me wonder how lenses could've failed you.
Then you call applyAuthToken right before serializing your Request type to a JSON ByteString.
edit: sorry I see that you wanted the auth token in the body not the headers. Its the exact same concept but you would make the `body` field in `Request` into some other specific type with a ToJSON instance and a slightly different `applyAuthToken` function.
> But ideally, you'd abstract out the auth stuff and put it in a central location which injects it into all requests that need it, eg. if you're using axios you'd add an interceptor for it. We couldn't find a way to do this without making the request into a Dictionary<string, obj>.
I do this kind of thing in Scala using Shapeless records sometimes. You can work pretty easily with types like "this case class, plus a field called token of type String", though writing those types down is a pain (they're basically recursive hlists of fields tagged with singleton types for their names).
Apparently GHC.Generic is the closest Haskell equivalent?
Not that it matters now, but I’ve recently learned about generic-data-surgery [1], a library that let’s you take a record X and return a record X with an added field Y. Pretty cool stuff, considering it’s all strongly typed. You would need to do the JSON serialisation _after_ the addToken step, but that shouldn’t be a problem.
I think I’m functional languages, or more generally languages that require you to very carefully express your types, that you have to avoid dealing in JSON except at the very edges of your program. If you do truly need dynamic content that can be a real pain but usually there is an okay way to express it - you just have to throw away the structure.
I'm okay with throwing away structure sometimes - it does make sense when, if you're at the point where you need to manipulate the output JSON while being abstract enough to not care about the input, I could see why you'd want to ignore it. But when the language syntax makes that hard or inelegant to do it's kind of a pain.
As a point of comparison, ReasonML and ReScript both have great ways to express JS-like objects with types (OCaml, which both are based on, suffers from a slightly more verbose syntax), and TypeScript of course leads the way in making dynamic-ish objects still work. Ideally I'd find a functional language with row polymorphism that also had a great ecosystem, but that's asking for too much.
> Ideally I'd find a functional language with row polymorphism that also had a great ecosystem, but that's asking for too much.
Your best bet here would be Rescript, OCaml or Purescript and leverage the JS ecosystem. There's also http://www.ocamljava.org/, which seems to be not really active. I'm not sure if I would call any option great though.
I can't speak for F#, but in Haskell we typically use the Aeson library for working with JSON data. You define a custom type to represent your JSON data and then you write a parser to go from JSON data serialized as `ByteStrings` into your custom type. The parser is implemented in a Typeclass and you get to leverage adhoc polymorphism to map your serialized JSON into whatever type (or complex set of nested types) you need in a given context.
`Value` is not particularly ergonomic but what you can do is parse the serialized JSON into `Value`, inject your additional JSON values into the `Object` HashMap, and then finally parse the `Value` term into whatever custom type you created for your application.
Haskell has a mature library for JSON, Aeson. I often use it to interface with APIs. You write your type, then functions to serialize/deserialize. Records using simple types (Text, Int, Maybe, etc.) can automatically derive the serialize/deserialize functions using generics.
Funny you mention PDF, because I’m doing that right now in Haskell for a resume builder project. There’s a few libraries for working with PDFs. HPDF (https://hackage.haskell.org/package/HPDF) supports all the basic functionality you’d expect to create PDFs.
It seems like you're asking a more general question unrelated to JSON. Rather you want to know how to manipulate data without all the ceremony of a strong type system, as you would in JavaScript or python. This is completely valid and is the best choice for simple jobs.
If you have a set of key value pairs where all the values are the same, you can use a Map or HashMap. These are both easily serialisable.
I'm not sure there's a way of having key-value pairs without a schema and with different types of values. If it has a schema you can use a record of course.
If you just want to add a single value, such as pdfUrl for example, you can include this as a nullable (Maybe Text) value in the record. Receive the data without it and return it with it. If you wanted to, you could also use a type parameter to guarantee that a pdfUrl is always included in the response record.
> If you have a set of key value pairs where all the values are the same, you can use a Map or HashMap. These are both easily serialisable.
This is ultimately the choice most people suggested back then, but I have two issues with it:
1. It leads to a ton of boilerplate, first in the step that converts the request record to a map, then adding stuff to the map and then serializing it. Instead, consider what just adding a field to what you know is a record is in F#:
let newRecord = {| oldRecord with token = token |}
This is vey close to the spread syntax of JS, and is super intuitive and elegant to write.
2. You lose type safety: say your JSON object has a bunch of things of different types within it, like a record with a few records or arrays inside. If you want to convert that to a map, you effectively have to make it a Dictionary<string, obj>, or whatever the Haskell equivalent for obj would be (obj is the "base class" in F#: everything is an obj.) That means you completely lose type information from that point on.
The closest any functional language gets to what I would consider "ideal" in this scenario is ReScript, which allows you to type row polymorphic open objects and use JS-like syntax to work with them. This is in addition to good ol' records, of course. Reason is pretty good, in this aspect, too.
Do you have worries that eventually your work will need to scale out to multiple people and then it might not be possible to find Haskell hackers?
I am thinking to make a new digital product in Rust but I am a bit worried that when time comes to scale up with more devs there will not be as many as there are with Go which are also less than Node devs.
There will certainly be less developers in the Haskell vs Go vs Javascript communities. However, you probably only need to find a few developers regardless of the language you are using. :)
That said, hiring Haskell engineers has its pros and cons. Its true there is a smaller pool of developers then other languages, but you end up with a smaller pool of very excited and committed developers eager to use Haskell in production.
No, I’m an indie hacker that also does some contracting. I am a one-man shop, so I don’t need to worry about hiring or working with others. I code in Haskell primarily because I enjoy it, the safety guarantees let me sleep at night, and I find it aesthetically pleasing.
There are plenty of Haskell developers, though not as many as more popular languages. Hiring for Haskell is more competitive, but easier to attract developer attention.
Can I ask why you chose Haskell for your webapps? I used Haskell for 18 months at a job a few years ago and found it was really awkward to find well-supported libraries for 'boring things' that pop up when dealing with a mildly complex webapp.
I enjoy it, the safety guarantees let me sleep at night, and the package APIs don’t constantly churn.
The only thing that I usually can’t find a library for is API clients for various services. Most companies don’t make a haskell client. In these cases I have to make HTTP calls to their API myself.
You are moving the goalposts: showing a webpage on a screen does not require four libraries.
This is a web app that renders different pages depending on database results and user preferences, automates rate limiting and has an ORM for type-safe SQL queries. I don't think four libraries is all that many, certainly not compared to say Ruby or NodeJS.
Selda, Blaze & WAI are far from being esoteric, don't mistake the boundaries of your knowledge for an objective line in the sand between what is esoteric and what is not.
Please tell me how you would create a website in any language with db connection without using any libraries sanely. Just serving static pages is not a webapp btw.
If downtime is permitted, I just write a script to migrate the data and run it. I prefer to write this script in SQL, but if that’s not possible I’ll just use Haskell or shell. Often I can use this approach with no downtime leveraging a proxy server/load balancer, if the migration is quick.
If zero downtime is required, I write the code to migrate the data to the new schema. Then update the app to write to the new schema while reading from old and new schemas. Then in a subsequent deploy after data is migrated, I write code that removes support for reading from the old schema.
How do you package these things up? I found Haskell's build system having some shortcomings (like for example I could not compile a package on rpi4 because it was running out of memory). I also had hard time with cross compilation, MacOS vs Linux.
It’s pretty much just Scotty, but it’s parameterized by an environment (state) that can be read from actions/routes, and doesn’t use Monad transformers.
Great post, and really nice to see what you've released as open source as well.
One thing I'd be interested to know - why did you go with Haskell over F#?
If this made you interested in trying out haskell for making web apps, check out IHP https://ihp.digitallyinduced.com/ It's the easiest way to get started with building haskell web apps :) IHP is designed for non-haskellers, so it's easy to start with for most people that haven't written much haskell yet.
I found about IHP a while ago and it sounds exactly like what I've been looking for in a web framework: Beginner-friendly, stable, type-safe, functional.
I got even more interested after I read your blog post on Auto Refresh[1], which sounds like it was heavily inspired by Phoenix's LiveView[2] (even though you don't mention it anywhere). Can you do a comparison between the two, in terms of how ergonomic and performant they are, etc.? Any claims to real-time interactivity?
Another, unrelated question: I'd like to use something like HSX but I hate the verbosity of HTML. I'd much rather write Haskell or something like Pug[3] (formerly Jade). How much friction would there be in switching to one of these instead?
How much "baggage" does Haskell carry as a language?
I have been Haskell-curious for a while. C++ is my language of choice, but it's just because it's the devil I know (e.g. so many of the bizarre design decisions like how r-value reference came to be, how =auto const&= can bind to an r-value and an l-value and you can't really tell, i.e. the "r-value lifetime disaster" [0], allowing operator& to be overloaded, a lack of ABI versioning leading to the standard library being unable to evolve [1]).
I know Rust is similar to the C++ use case, but the mental model jump is just not appealing enough at the moment.
I have watched some stuff about Haskell where people opine about how certain standard library stuff causes strange behavior (e.g. lazy + head of empty list = exception when the list is eventually evaluated elsewhere, weird performance things I don't understand, etc).
Is the list of "secret language and library disasters and workarounds" long in Haskell?
That sort of knowledge that is actually contrary to the language tutorial path (e.g. "no one uses the standard library, we all link this other one to avoid issues X and Y") makes language learning a bit frustrating.
56 comments
[ 0.79 ms ] story [ 161 ms ] threadI'm not sure if the poster is from NoRedInk, but my only feedback is that I'd love to see some sample code that demonstrates the power of the handle pattern in use?
I followed the link that discussed the pattern which then referenced this https://github.com/jaspervdj/fugacious. It would be better tho if there was a concise sample.
- Twain (like Sinatra) for server and routing: https://hackage.haskell.org/package/twain
- WAI middleware for things like static servers, rate-limiting, body-parsing, etc.: https://hackage.haskell.org/package/wai-extra and https://hackage.haskell.org/package/wai-middleware-static
- Blaze-html for writing HTML views: https://hackage.haskell.org/package/blaze-html
- Selda for a LINQ-style type-safe SQL interface to PostgreSQL and SQLite: https://selda.link
I’d be happy to answer any other questions about developing web apps with Haskell or libraries I use.
- Did you use anything else than Haskell? If so what made you switch to Haskell?
- How do you handle JS? Just write JS? TS? Haskell compiled to JS?
- Are there any challenges specific to Haskell?
- I try to avoid JS. I do progressive enhancement with vanilla JS, and avoid JS webapps unless absolutely necessary. If I need to write a JS webapp, I use a purely functional approach. I found that when writing React, using frameworks, using TypeScript or PureScript, I lost so much time and effort managing my build step and fighting with APIs and package churn. I use preact with the htm library, vanilla JS, and most importantly no build step and no npm. I don’t use a framework. I use a purely functional approach inspired from Haskell/Elm. I have an update function which returns a new state and effects from the current state and event, and a dispatch function which is attached to onevent props. The dispatch function calls the update function, renders, and runs effects. These few functions are 20 lines of code.
- No challenges specific to Haskell so far. Of course I had to learn the ecosystem when I started, which packages to use, language and tooling gotchas, etc. but that is the case for any language.
Here's an example of a working app using my approach I described above: https://jsbin.com/putonutica/edit?js,output
The two functions, render and dispatch, are essentially the only code I share across the projects. That's the whole "framework". It's 19 lines of code. All it requires is a link to Preact, which is 14K. Oh, and I just keep everything in one big file. Simple, but I've built multiple complex single-page JS apps with this approach.
I'm sure someone will tell me I'm doing it all wrong, and I need to use Next.js with webpack, a giant stack of npm packages, code-splitting and bundling, and whatever craziness front-end developers are doing today.
Another question that came to mind: how good is the ecosystem for Haskell? Eg, if you wanted to deal with, say, generating PDFs, would you be more or less confident that you could get something working over a weekend?
E: Completely forgot to mention, sumi.news looks great!
That's something lenses look purpose-built to do. Did they fail you, too?
As an aside, I chuckled at the 'Did they fail you, too?'; it made me wonder how lenses could've failed you.
You would probably have some Request with a ToJSON instance and Token types such as:
Then you might write a function to add the token header: Or if you really wanted to use a lens you could do: Then you call applyAuthToken right before serializing your Request type to a JSON ByteString.edit: sorry I see that you wanted the auth token in the body not the headers. Its the exact same concept but you would make the `body` field in `Request` into some other specific type with a ToJSON instance and a slightly different `applyAuthToken` function.
I do this kind of thing in Scala using Shapeless records sometimes. You can work pretty easily with types like "this case class, plus a field called token of type String", though writing those types down is a pain (they're basically recursive hlists of fields tagged with singleton types for their names).
Apparently GHC.Generic is the closest Haskell equivalent?
[1]: https://hackage.haskell.org/package/generic-data-surgery-0.3...
As a point of comparison, ReasonML and ReScript both have great ways to express JS-like objects with types (OCaml, which both are based on, suffers from a slightly more verbose syntax), and TypeScript of course leads the way in making dynamic-ish objects still work. Ideally I'd find a functional language with row polymorphism that also had a great ecosystem, but that's asking for too much.
Your best bet here would be Rescript, OCaml or Purescript and leverage the JS ecosystem. There's also http://www.ocamljava.org/, which seems to be not really active. I'm not sure if I would call any option great though.
Now to your specific question, Aeson comes with a type called `Value` which represents JSON data directly. You can see the definition of `Value` [here](https://hackage.haskell.org/package/aeson-1.5.6.0/docs/src/D...).
`Value` is not particularly ergonomic but what you can do is parse the serialized JSON into `Value`, inject your additional JSON values into the `Object` HashMap, and then finally parse the `Value` term into whatever custom type you created for your application.
Haskell has a mature library for JSON, Aeson. I often use it to interface with APIs. You write your type, then functions to serialize/deserialize. Records using simple types (Text, Int, Maybe, etc.) can automatically derive the serialize/deserialize functions using generics.
Funny you mention PDF, because I’m doing that right now in Haskell for a resume builder project. There’s a few libraries for working with PDFs. HPDF (https://hackage.haskell.org/package/HPDF) supports all the basic functionality you’d expect to create PDFs.
If you have a set of key value pairs where all the values are the same, you can use a Map or HashMap. These are both easily serialisable.
I'm not sure there's a way of having key-value pairs without a schema and with different types of values. If it has a schema you can use a record of course.
If you just want to add a single value, such as pdfUrl for example, you can include this as a nullable (Maybe Text) value in the record. Receive the data without it and return it with it. If you wanted to, you could also use a type parameter to guarantee that a pdfUrl is always included in the response record.
This is ultimately the choice most people suggested back then, but I have two issues with it:
1. It leads to a ton of boilerplate, first in the step that converts the request record to a map, then adding stuff to the map and then serializing it. Instead, consider what just adding a field to what you know is a record is in F#:
This is vey close to the spread syntax of JS, and is super intuitive and elegant to write.2. You lose type safety: say your JSON object has a bunch of things of different types within it, like a record with a few records or arrays inside. If you want to convert that to a map, you effectively have to make it a Dictionary<string, obj>, or whatever the Haskell equivalent for obj would be (obj is the "base class" in F#: everything is an obj.) That means you completely lose type information from that point on.
The closest any functional language gets to what I would consider "ideal" in this scenario is ReScript, which allows you to type row polymorphic open objects and use JS-like syntax to work with them. This is in addition to good ol' records, of course. Reason is pretty good, in this aspect, too.
The closest any functional language gets to this
The syntax isn't great, admittedly, but if you're willing to write a template haskell macro you could probably get something like
working.I am thinking to make a new digital product in Rust but I am a bit worried that when time comes to scale up with more devs there will not be as many as there are with Go which are also less than Node devs.
That said, hiring Haskell engineers has its pros and cons. Its true there is a smaller pool of developers then other languages, but you end up with a smaller pool of very excited and committed developers eager to use Haskell in production.
There are plenty of Haskell developers, though not as many as more popular languages. Hiring for Haskell is more competitive, but easier to attract developer attention.
The only thing that I usually can’t find a library for is API clients for various services. Most companies don’t make a haskell client. In these cases I have to make HTTP calls to their API myself.
To be honest, this reads like satire.
Throwing a webpage on a screen shouldn't require four esoteric libraries.
This is a web app that renders different pages depending on database results and user preferences, automates rate limiting and has an ORM for type-safe SQL queries. I don't think four libraries is all that many, certainly not compared to say Ruby or NodeJS.
Requires a static html-file
If zero downtime is required, I write the code to migrate the data to the new schema. Then update the app to write to the new schema while reading from old and new schemas. Then in a subsequent deploy after data is migrated, I write code that removes support for reading from the old schema.
(edit) sumi.news works well on a potato phone, well done!
I am wary of large frameworks, so I have not tried IHP. It looks nice though.
We just started on a project and Servant felt like overkill, but this is the first I’ve heard of Twain.
I got even more interested after I read your blog post on Auto Refresh[1], which sounds like it was heavily inspired by Phoenix's LiveView[2] (even though you don't mention it anywhere). Can you do a comparison between the two, in terms of how ergonomic and performant they are, etc.? Any claims to real-time interactivity?
Another, unrelated question: I'd like to use something like HSX but I hate the verbosity of HTML. I'd much rather write Haskell or something like Pug[3] (formerly Jade). How much friction would there be in switching to one of these instead?
[1]: https://ihp.digitallyinduced.com/ShowPost?postId=a2d56dc7-d2...
[2]: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
[3]: https://pugjs.org/
I have been Haskell-curious for a while. C++ is my language of choice, but it's just because it's the devil I know (e.g. so many of the bizarre design decisions like how r-value reference came to be, how =auto const&= can bind to an r-value and an l-value and you can't really tell, i.e. the "r-value lifetime disaster" [0], allowing operator& to be overloaded, a lack of ABI versioning leading to the standard library being unable to evolve [1]).
I know Rust is similar to the C++ use case, but the mental model jump is just not appealing enough at the moment.
I have watched some stuff about Haskell where people opine about how certain standard library stuff causes strange behavior (e.g. lazy + head of empty list = exception when the list is eventually evaluated elsewhere, weird performance things I don't understand, etc).
Is the list of "secret language and library disasters and workarounds" long in Haskell? That sort of knowledge that is actually contrary to the language tutorial path (e.g. "no one uses the standard library, we all link this other one to avoid issues X and Y") makes language learning a bit frustrating.
[0] https://quuxplusone.github.io/blog/2020/03/04/rvalue-lifetim... [1] https://www.youtube.com/watch?v=-XjUiLgJE2Y
https://www.snoyman.com/blog/2020/10/haskell-bad-parts-1/
https://www.snoyman.com/blog/2020/11/haskell-bad-parts-2/
https://www.snoyman.com/blog/2020/12/haskell-bad-parts-3/