recollections: Fixed-size representable (Zippy Applicative) collections.

[ bsd3, data-structures, library ] [ Propose Tags ] [ Report a vulnerability ]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.1.1.0
Change log CHANGELOG.md
Dependencies base (>=4.18 && <5), template-haskell (>=2.20) [details]
License BSD-3-Clause
Copyright 2026 IC Rainbow
Author IC Rainbow
Maintainer aenor.realm@gmail.com
Uploaded by AlexanderBondarenko at 2026-10-04T22:59:00Z
Category Data Structures
Source repo head: git clone https://gitlab.com/dpwiz/recollections
Distributions
Downloads 20 total (12 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-10-04 [all 1 reports]

Readme for recollections-0.1.1.0

[back to package description]

recollections

The splicing module needs these extensions and imports:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia #-}

import GHC.Generics (Generically1(..))

Given an enumeration of things like

data Things
  = This
  | That
  | Something
  | Else
  | Entirely
  deriving (Eq, Ord, Show, Enum, Bounded, Generic)

We can derive a record that will behave like a homogenous zip-list of static size:

mkCollection ''Things

-- data Collection a = Collection
--   { this, that, something, else', entirely :: a
--   }
--   deriving stock (Eq, Show, Generic1, Functor, Foldable, Traversable)
--   deriving Applicative via (Generically1 Collection)

-- Fill the record with its indices
mkIndices ''Things

Nesting

A constructor may wrap the tag type of another collection. The field then holds that whole collection:

import Some qualified

data Tag
  = Something Some.Tag
  | Local
  deriving (Eq, Ord, Show)

mkCollection ''Tag

-- data Collection a = Collection
--   { something :: Some.Collection a
--   , local :: a
--   }

The nested collection is looked up in the module defining Some.Tag, so the nested tag and its splices must live together in their own module (and be exported from it). Every collection generator spliced for the outer tag needs its counterpart spliced for the nested one. The class instance generators delegate to the nested instances instead, so mkDistributive and mkRepresentable need the same instances for Some.Collection.

Stock Enum and Bounded can't be derived for such tags, so there are generators that number the nested tags in place:

mkBounded ''Tag
mkEnum ''Tag -- needs Bounded Tag, plus Enum and Bounded Some.Tag

-- [minBound .. maxBound] == toList indices

Kmettoverse

With adjunctions and distributive in the dependencies of the splicing package (recollections itself doesn't depend on them), the classes can be instantiated directly:

{-# LANGUAGE TypeFamilies #-}

import Data.Distributive (Distributive(..))
import Data.Functor.Rep (Representable(..))

mkDistributive ''Things
mkRepresentable ''Things

-- instance Distributive Collection where ...
-- instance Representable Collection where
--   type Rep Collection = Things
--   ...

Representable requires the Distributive instance, so both splices are needed.

The classes are looked up in the splicing module scope, by their plain or module-qualified names. When the modules are imported under an alias, pass the class names explicitly:

import qualified Data.Distributive as D
import qualified Data.Functor.Rep as R

mkDistributiveFor ''D.Distributive ''Things
mkRepresentableFor ''R.Representable ''Things

Kmettoverse-lite

Without the extra dependencies, the methods can be generated as top-level functions instead. Those names clash with the class imports above.

-- Distributive
mkDistribute ''Things
-- collect is `distribute . fmap f`

-- Representable
mkIndex ''Things
mkTabulate ''Things

VS

  • Vanilla monomorphic records: don't have functor/foldable/traversable/applicative goodies.
  • Hand-rolling the functor: be my guest, hand-roll the field enum too, while you're at it.
  • Map Things a: All fields are mandatory, all keys are known -> lookups are total and O(1). The optional fields are recoverable, with Collection (Maybe a).
  • Things -> a: The collection can be stored and loaded.

Gotchas

All the constructors of the tag type must be nullary or wrap a single nested tag type — anything else is a compile-time error, since a partial Collection would make index partial too.

The generated names (Collection, indices, distribute, index, tabulate) are fixed, so a tag named e.g. Index will clash with them. For the same reason, a module with nested collections must be imported qualified, or its generated names become ambiguous with the outer ones.