6 ms·
I code webapps in Haskell (latest is https://sumi.news https://sumi.news). My stack is simple: - Twain (like Sinatra) for server and routing: https://hackage.h
by tofukid 5y ago
I code webapps in Haskell (latest is https://sumi.news https://sumi.news). My stack is simple:
- Twain (like Sinatra) for server and routing: https://hackage.haskell.org/package/twain 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 https://hackage.haskell.org/package/wai-extra and https://hackage.haskell.org/package/wai-middleware-static https://hackage.haskell.org/package/wai-middleware-static
- Blaze-html for writing HTML views: https://hackage.haskell.org/package/blaze-html https://hackage.haskell.org/package/blaze-html
- Selda for a LINQ-style type-safe SQL interface to PostgreSQL and SQLite: https://selda.link https://selda.link
I’d be happy to answer any other questions about developing web apps with Haskell or libraries I use.
- otabdeveloper4 5y ago> My stack is simple To be honest, this reads like satire.
- tasuki 5y agoWhy? They mention like four libraries. Is your stack simpler?
- otabdeveloper4 5y agoThis isn't nonlinear dynamic rocket surgery you're doing here. Throwing a webpage on a screen shouldn't require four esoteric libraries.
- WJW 5y agoYou 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.
- Buttons840 5y agoWhat stack do you propose?
- Vosporos 5y agoSelda, 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.
- rowanG077 5y agoPlease 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.
- cannabis_sam 5y ago> Throwing a webpage on a screen Requires a static html-file
- Zababa 5y agoA few questions: - 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?
- tofukid 5y ago- 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.
- Zababa 5y agoThanks for the detailed answer. The way you handle JS is very interesting.
- tofukid 5y agoThe 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. Here's an example of a working app using my approach I described above: https://jsbin.com/putonutica/edit?js,output 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.
- square_usual 5y agoI 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!
- nine_k 5y ago> injecting a value into an object That's something lenses look purpose-built to do. Did they fail you, too?
- square_usual 5y agoThe 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.
- solomonb 5y agoLenses are definitely not necessary to do what you want here. In haskell, you typically keep the JSON data to the very periphery of your program. You would probably have some Request with a ToJSON instance and Token types such as: data Request = Request { url :: URI , method :: Method , headers :: [Header] , body :: ByteString ... } instance ToJSON Request where toJSON = ... newtype Token = Token ByteString Then you might write a function to add the token header: applyAuthToken :: Token -> Request -> Request applyAuthToken (Token tok) req = req { headers = ("token", tok) : req headers } Or if you really wanted to use a lens you could do: headersL :: Lens' Request [Header] headersL = lens headers (\req hdrs -> req { headers = hdrs }) applyAuthToken :: Token -> Request -> Request applyAuthToken (Token tok) req = over headersL ("token", tok) :) req 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.
- axelroze 5y agoDo 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.
- solomonb 5y agoThere 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.
- tofukid 5y agoNo, 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.
- exdsq 5y agoCan 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.
- tofukid 5y agoI 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.
- Buttons840 5y agoWhat do you do for database migrations?
- tofukid 5y agoIf 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.
- scns 5y agoHave you tried IHP (mentioned in a toplevel comment) yet? If yes, whats your opinion on it? (edit) sumi.news works well on a potato phone, well done!
- tofukid 5y agoThanks for the feedback, I’m glad sumi.news works well on your device. I am wary of large frameworks, so I have not tried IHP. It looks nice though.
- StreamBright 5y agoHow 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.
- tofukid 5y agoI use a GitHub action to build, send the new binary to a server, and then restart the app.
- pchiusano 5y agoCool. How does Twain compare to Scotty? We just started on a project and Servant felt like overkill, but this is the first I’ve heard of Twain.
- tofukid 5y agoIt’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.