Remix.run Logo
birdfood 4 days ago

OCaml is probably my favourite language.

The most involved project I did with it was a CRUD app for organising Writer's Festivals.

The app was 100% OCaml (ReasonML so I could get JSX) + Dream + HTMX + DataTables. I used modules to get reusable front end templates. I loved being able to make a change to one of my data models and have the compiler tell me almost instantly where the change broke the front end. The main value of the app was getting data out of excel into a structured database, but I was also able to provide templated and branded itineraries in .odt format, create in memory zipped downloads so that I didn't need to touch the server disk. I was really impressed by how much I could achieve with the ecosystem.

But having to write all my database queries in strings and then marshal the data through types was tiring (and effectively not compile time type checked) and I had to roll my own auth. I often felt like I was having to work on things that were not core to the product I was trying to build.

I've spent a few years bouncing around different languages and I think my take away is that there is no perfect language. They all suck in their own special way.

Now I'm building an app just for me and I'm using Rails. Pretty much everything I've wanted to reach for has a good default answer. I really feel like I'm focused on what is relevant to the product I'm building and I'm thinking about things unrelated to language like design layout and actually shipping the thing.

BenGosub 4 days ago | parent [-]

What is the idiomatic way to handle the results from the database in a strongly typed functional language?

birdfood a day ago | parent [-]

I certainly can’t say whether this is idiomatic as I was working it all out myself. But I’d basically write a type for each operation (as I’d read elsewhere). And honestly this was a bit of a drag too. I really wanted some reflection to make generating this code more ergonomic. From memory I’d have types like these

Author id: int, name: string, book_id: int

NewAuthor name: string, book_id: int

ViewAuthor name: string, book_title: string

Author represents the data in the db, NewAuthor allows for an insert operation, and ViewAuthor is for showing the data to a user.

You could argue for combing Author and NewAuthor and making id optional but I wanted to enforce at the type level that I was working with stored data without needing to check id everywhere.