Rel8 released

Hello, (belated) happy new year everyone!

We’ve just released Rel8 Rel8 is a Haskell library for interacting with PostgreSQL databases, built on top of the fantastic Opaleye library.

There are a lot of goodies in this release, including support for window functions, GHC 9.4 support, bug fixes, and more. For a complete list of changes, see the release notes.

Happy querying!


Window functions are very useful, glad that they’re now supported!

For Nix users who want a quick Rel8 playground, this repo’s shell.nix sets up Postgres and loads the “Pagila” example database. File Rel8Main.hs contains Rel8 TableSchemas for the main tables in the database, from which to construct Querys using each.


@ocharles While trying to wrap my head around Rel8, I wrote the following paragraphs to put my ideas in order. Would you say that they are a correct description of how the Query monad works?

Query, while being a monad, is a carefully controlled one. Everything bound to the left of <- won’t be a “plain” value, but one wrapped in an Expr context. Primitive types are wrapped like Expr Int. Record types will have each of its components wrapped in Expr, and it will look like Person Expr. We can also have tuples like (Expr Int, Person Expr).

Inside the Query monad, you won’t pattern match on sum types directly, in the conventional way, using case. Instead, you’ll use functions like maybeTable or deconstructADT that “shift” the pattern-match into the Expr world.

Also, inside Query type signatures, you shouldn’t use constructors like Maybe to signify the absence of a value, or Either to signify alternatives. Instead of them, you need to use MaybeTable and EitherTable. A common source of MaybeTables is the optional function.

We can think of select as a function that removes Expr wrappers, giving us direct access to values, and also converts MaybeTable and EitherTable into Maybe and Either, respectively.

So that’s definitely not true. Consider a <- pure (1 :: Int), for example. a :: Int isn’t wrapped in an "Expr context". I think it is true, however, that the value of anything bound to the left of <- that’s not in an "Expr context", will be statically known with respect to the query, i.e. its value is known before the query runs.

1 Like

:man_facepalming: You’re right, of course. And you can pattern-match normally on them as well. I didn’t think of that because I only use Query's pure for recombining Expr-wrapped values previously obtained from the SQL side.

1 Like

It’s hard to give a definitive answer, but it looks like your intuition is right. It’s difficult to say things like “you don’t use case when in the Query monad”, because there are many times when you can/will do that (e.g., when combining multiple Exprs into a Table and later unpacking them with pattern matching). Other than that I think you’ve got the right idea!