| Copyright | (c) 2026 The Polytropos Authors |
|---|---|
| License | EUPL-1.2 OR Apache-2.0 |
| Maintainer | nadia.yvette.chambers@ik.me |
| Stability | experimental |
| Portability | non-portable (GHC extensions) |
| Safe Haskell | None |
| Language | GHC2021 |
Polytropos.Hasql
Description
Integrates polytropos open records with the high-performance
PostgreSQL driver hasql.
Architectural Overview & Rationale
Bypassing Tuple Limits & Intermediate Allocations
Conventional database libraries map SQL rows to Haskell data types via intermediate
tuples (e.g. (a, b, c)). This imposes GHC's 62-column tuple limit and generates
large numbers of short-lived heap objects.
recordRow builds a that consumes one column per row
field, in row order, and packs the decoded values straight into the
contiguous Row (Record r)SmallArray payload of the record. The column decoders are
combined by the AllRowDecoder constraint walk in a single pass, appending one slot
at a time. Rows of arbitrary arity (50, 100, 200+ columns) decode with zero tuple overhead.
Each column picks its value decoder from the Column class at that
field's own type, allowing heterogeneous column rows.
When to Use This Module
- When executing high-throughput PostgreSQL queries with
hasqlwhere rows have many columns or where open records are used as query results.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
import qualified Hasql.Decoders as HD
import Polytropos
import Polytropos.Hasql
type UserRow = Record '["id" ':= Int64, "age" ':= Int32]
userDecoder :: HD.Row UserRow
userDecoder = recordRow @UserRowSynopsis
- type HasqlRow = Row
- recordRow :: forall (r :: Row). AllRowDecoder r => HasqlRow (Record r)
- class AllRowDecoder (r :: Row) where
- class Column a where
- columnValue :: Value a
Row Decoder Construction
hasql's row decoder monad; aliased to avoid clashing with the Row
kind synonym from Polytropos.Core.
recordRow :: forall (r :: Row). AllRowDecoder r => HasqlRow (Record r) Source #
Build a hasql row decoder that consumes one column per field of the
row, in row order, and materialises the record directly: the decoded
slots go straight into a contiguous SmallArray payload via
productRep. No intermediate tuples — the walk composes the column
decoders applicatively, so arity is unbounded.
Column-Decoder Walk
class AllRowDecoder (r :: Row) where Source #
Heterogeneous column walk: every field of the row r has a Column
instance at its own type. genRow composes one Row column decoder per
field, in row order, collecting the decoded values as erased payload
slots — a single pass, one slot appended per column, no tuples.
Instances
| AllRowDecoder ('[] :: [Pair Symbol Type]) Source # | |
| (Column a, AllRowDecoder r) => AllRowDecoder ((n ':= a) ': r) Source # | |
Per-Column Value Decoders
The hasql value decoder for a column of type a. Instances pick the
Postgres column type; the row walk only needs the column combinator.
Methods
columnValue :: Value a Source #
Instances
| Column Int16 Source # | |
Defined in Polytropos.Hasql Methods columnValue :: Value Int16 Source # | |
| Column Int32 Source # | |
Defined in Polytropos.Hasql Methods columnValue :: Value Int32 Source # | |
| Column Int64 Source # | |
Defined in Polytropos.Hasql Methods columnValue :: Value Int64 Source # | |