7 ms·
Important bit missing from the title: this is using Haskell (and Nix). IHP is supposed to become the Django/Rails/Phoenix of Haskell. I've been using Django p
by hendi_ 6y ago
Important bit missing from the title: this is using Haskell (and Nix).
IHP is supposed to become the Django/Rails/Phoenix of Haskell.
I've been using Django professionally for since 2013, but have started using IHP a couple of weeks ago. It's still quite early but with surprisingly few rough edges, i.e. the developer ergonomics are much better than I expected. It has great documentation that is improving rapidly (as opposed to many other Haskell libraries, which provide little more than API docs or even just the typed function definitions) and offers a refreshing take on database management and migrations.
Some of its killer features:
- HSX, a JSX-like template language that looks like HTML while providing type safety
- Auto live reloading without the need to setup anything
- Documentation with examples: it lets you query the database without learning about monads
- it defines |> for you ;-)
- type-safe, composable SQL queries:
projects <- query @Project
|> filterWhere (#userId, userId)
|> filterWhere (#deleted, False)
|> orderBy #createdAt
|> fetch
- deleted 6y ago[deleted]
- dang 6y ago(We put those in the title after this was posted.)
- throwaway_pdp09 6y agoI get the type-safe part, assuming it means the eventual query will produce a predictable result set compatible with wherever it's being read into (I'm not a haskeller), but a) SQL is easy enough to learn syntactically b) there are good reasons not to create dynamic SQL; it's better to write it such that a query plan can be cached (for scalability). Which is very likely possible here but is it done? I wonder if the person who wrote this understands SQL to sufficient depth. Not saying either way, just asking.
- hendi_ 6y agoYour point a) is moot, this is not what this is about. When writing manual SQL nothing stops you from doing `SELECT * FROM persons WHERE age > 'eighteen'` (the `age` field is a number, obviously). Coming from Django I was often bitten by `Person.objects.filter(age__gt=trashhold) # boom!` (note the misspelled variable "threshold") or, if you prefer manual writin SQL: `Person.objects.raw("SELECT * FROM persons WHERE age > {}", trashhold) # boom! as well`. (Admittedly this is unrelated to type safety, just a disadvantage of Python being interpreted, but still something that Haskell/IHP protects me of.) Regarding b), yes there are good reasons for prepared statements. I won't go into your argument "for scalability" (premature optimization cough) but cached query plans aren't perfect either: data and data typologies change, and so does the optimal query plan. See https://blog.soykaf.com/post/postgresql-elixir-troubles/ https://blog.soykaf.com/post/postgresql-elixir-troubles/ for a blog post about Elixir/Phoenix/Ecto users being bitten by that. > I wonder if the person who wrote this understands SQL to sufficient depth. Not saying either way, just asking. I did not write the IHP code, just the above comment. But I've read many of CJ Date's books and have a good grasp on the relational model, and of SQL too. I simply don't see things as black/white, there are certain queries where I do prefer prepared statements and functions, or PL/pgSQL even, then there are ones where I use an external .sql file to keep things tidy. But most of the time (especially in the user facing part of a web app) I like my ORMs or SQL DSLs.
- necovek 6y agoUnless you've got `trashhold` actually defined, tools like flake8 would protect you from that mistake with Python. Using type hints would also protect you from using variables with incompatible types with a better designed API (Django's ORM API is anything but). As for SQL performance, I've usually only worked with up to hundreds of millions of rows per table, and the most important thing at that scale was to attempt to keep indexes in memory cache (that improves or degrades the performance by a couple of orders of magnitude). I haven't had a need for query plans to be cached, though I did use subselects to force particular query plans when needed (which has the similar problem of query plans not being the most efficient ones when data evolves sufficiently).
- fabian2k 6y agoYou really can't avoid dynamic SQL if you need it for your software. For example, if you give your users the ability to select filters to apply to the data fetched from the DB, you will have to dynamically create the SQL unless the number of possible filters is extremely low. Above a certain scale this might not be feasible for predictable performance, but not everything has billions of rows. The choice isn't to use dynamic SQL or not in these cases, it's whether you implement the feature that requires dynamic SQL or leave it out entirely.
- hombre_fatal 6y agoThough interestingly (as you acknowledge via "if you need it") that also tends to be the only time a traditional site/app does need it: a /search endpoint.
- sradman 6y ago> IHP is supposed to become the Django/Rails/Phoenix of Haskell. And like these other server-side web frameworks, IHP is SQL centric. What sets it apart is the IDE integrated into the dev server. The IDE together with Nix allows IHP to be PostgreSQL only, avoiding out-of-band server installs and providing a UI for schema/model construction. This is an interesting option and seems to have appeal beyond Haskell enthusiasts.
- rckoepke 6y agoTo help me understand better, what would be an example of an "out-of-band" server install?
- sradman 6y agoI should have qualified it as "RDBMS" server install. Most new SQL-centric web frameworks support three Open Source RDBMS engines: 1. MySQL, 2. PostgreSQL, and 3. SQLite. SQLite is rarely considered appropriate (arguably) for the server-side deployment RDBMS but its value comes from being embedded in the language runtime or web framework so it is available during development. IHP includes PostgreSQL as a Nix dependency so it is as ubiquitous as SQLite in the developer environment. "Out-of-Band" is relative to the project dependencies. Using PostgreSQL in the development environment for say Rails or Django requires "out-of-band" installation, configuration, and management. Frameworks like Rails and IHP apply the schema/migrations to a live development RDBMS and read the Catalog Metadata to generate the classes/structs used as the Data Access Objects (e.g. ActiveRecord in Rails). The RDBMS, therefore, is part of the CI/CD pipeline. Other ORMs define their entities independent of the RDBMS. I think Java JPA and Apple Core Data work this way; the SQL DDL is generated from the Entity Model. YeSQL-style frameworks also require a live development RDBMS but they extract metadata from each prepared statement rather than catalog metadata for each table which is mapped to a class/struct.
- abathur 6y agoI think sradman means that Nix enables IHP to guarantee the presence of postgres, rather than relying on the user to ensure pre-requisites get installed. Edit: Gah. :)
- hopia 6y agoThe SQL composition seems really intuitive. I'm pondering whether it's worth the effort to port my Servant application to this or not.