packages feed

polytropos-reader-soup-0.1.0.2: README.md

# polytropos-reader-soup

Composable multi-context Reader monad with zero-overhead row polymorphism for [polytropos](https://github.com/NadiaYvette/polytropos).

## Overview

In traditional Haskell application design, managing multiple configuration environments, connection pools, and runtime capabilities typically forces one of two unsatisfactory choices:
1. **MTL Class Proliferation:** Defining distinct `MonadDatabase`, `MonadConfig`, `MonadLogger`, `MonadMetrics` classes with $O(N^2)$ quadratic boilerplate instances across every layer of the transformer stack.
2. **Monolithic AppEnv:** Bundling everything into a single nominal `AppEnv` record, coupling small, self-contained sub-routines to the entire global environment and hindering modularity.

`polytropos-reader-soup` solves this dilemma by re-architecting Yves Parès's `reader-soup` on top of Polytropos's high-performance flat contiguous array representation (`Polytropos.Core.Rep`). A `ReaderSoup` hosts an arbitrary open row of labeled contexts in a single `ReaderT (Record ctxs) IO` layer. Subsystems declare exactly the slice of contexts they require, accessing them in $O(1)$ time via compile-time synthesized dictionary offsets, with structural narrowing via `filtering` taking $O(K)$ flat array copy time.

## Features

- **`ReaderSoup`**: Single-layer multi-context Reader environment (`ReaderT (Record ctxs) IO a`).
- **`askSoup`**: $O(1)$ context retrieval using type-level label symbols or `OverloadedRecordDot`.
- **`filtering`**: Zero-boilerplate structural subtyping via `SubRow`, allowing sub-actions to run seamlessly inside larger environments.
- **`Spoon`**: Isolates a single labeled context from the broth and equips it with a standard `MonadReader` instance supporting `ask` and `local`.
- **`SoupContext`**: Bridges existing third-party ReaderT-like monads (e.g. `ResourceT`, `KatipContextT`) via `picking`, `scooping`, and `pouring`.
- **`consumeSoup`**: Composes modular `ContextRunner` handlers to peel and execute contexts sequentially until the base broth is reached.
- **`Polytropos.ReaderSoup.Resource`**: Turnkey `resourcet` integration with `SoupContext InternalState ResourceT` and `MonadResource` instances.

## Quick Example

```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fplugin=Polytropos.Plugin #-}

module Main where

import Control.Monad.IO.Class (liftIO)
import Polytropos
import Polytropos.ReaderSoup

data DBConn = DBConn String
data Logger = Logger (String -> IO ())

type AppCtx =
  '[ "db"     := DBConn
   , "logger" := Logger
   , "port"   := Int
   ]

-- | A database worker only requires the DBConn context:
runQuery :: ReaderSoup '[ "db" := DBConn ] String
runQuery = do
  DBConn connStr <- askSoup (Field @"db")
  pure ("Executed on " ++ connStr)

-- | Main application running with the full AppCtx:
app :: ReaderSoup AppCtx ()
app = do
  Logger logFn <- askSoup (Field @"logger")
  liftIO (logFn "Application starting...")

  -- Narrow the environment from AppCtx down to '[ "db" := DBConn ]:
  res <- filtering runQuery
  liftIO (logFn ("Query result: " ++ res))

main :: IO ()
main = do
  let env = insertField (Field @"db") (DBConn "postgres://localhost:5432/prod")
          $ insertField (Field @"logger") (Logger putStrLn)
          $ insertField (Field @"port") (8080 :: Int)
          $ emptyRecord

  runReaderSoup app env
```

## Attribution & Precedents

This package is inspired by and directly acknowledges Yves Parès's work on `reader-soup` within the [Porcupine](https://github.com/funflow/porcupine) pipeline framework. The original package pioneered the concept of representing open collections of Reader-like monads via Vinyl extensible records. `polytropos-reader-soup` preserves those ergonomic and architectural insights while re-grounding them entirely on Polytropos's native flat array physics, compile-time literal index resolution, and structural `SubRow` evidence synthesis.

## License

Dual-licensed under `EUPL-1.2 OR Apache-2.0`.