tuple-append: A package to append, sequence and fold items and tuples into new tuples.

[ bsd3, library, utils ] [ Propose Tags ]

A library that makes it more convenient to append a tuple with m items and a tuple with n items into a tuple with m+n items, together with functions to add an element at the left, or the right side of a tuple and sequence or fold a tuple.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.0.0, 0.1.1.0, 0.1.2.0, 0.2.0.0, 0.3.0.0
Change log CHANGELOG.md
Dependencies base (>=4.7 && <5), ghc-prim (>=0.3.10), template-haskell (>=2.5.0.0) [details]
License BSD-3-Clause
Copyright 2022 Willem Van Onsem
Author Willem Van Onsem
Maintainer hapytexteu+gh@gmail.com
Category utils
Home page https://github.com/hapytex/tuple-append#readme
Source repo head: git clone https://github.com/hapytex/tuple-append
Uploaded by wvanonsem90 at 2023-05-28T22:58:19Z
Distributions NixOS:0.3.0.0
Reverse Dependencies 1 direct, 0 indirect [details]
Downloads 211 total (13 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2023-05-29 [all 1 reports]

Readme for tuple-append-0.3.0.0

[back to package description]

tuple-append

Build Status of the package by GitHub actions Hackage version badge

Usage

The package defines five typeclasses: TupleAddL, TupleAddR, TupleAppend, SequenceTuple, and FoldTuple. These are defined in the Data.Tuple.Append.Class module and re-exported in the Data.Tuple.Append module. These typeclasses define the functions (<++) ∷ x → (v₁, v₂, …, vₙ) → (x, v₁, v₂, …, vₙ), (++>) ∷ (v₁, v₂, …, vₙ) → x → (v₁, v₂, …, vₙ, x), (+++) ∷ (u₁, u₂, …, uₘ) → (v₁, v₂, …, vₙ) → (u₁, u₂, …, uₘ, v₁, v₂, …, vₙ), sequenceTupleA ∷ Applicative f ⇒ (f v₁, f v₂, …, f vₙ) → f (v₁, v₂, …, vₙ), sequenceTupleA_ ∷ Applicative f ⇒ (f v₁, f v₂, …, f vₙ) → f (), foldlTuple ∷ (a → v → a) → a -> (v₁, v₂, …, vₙ) → a, foldrTuple ∷ (v → a → a) → a -> (v₁, v₂, …, vₙ) → a, and foldMapTuple ∷ Monoid m ⇒ (a → m) → (v₁, v₂, …, vₙ) → m respectively. Functions that thus add an element to the left side or the right side of tuple; append two tuples together into a new tuple; sequence or fold a tuple. The Data.Tuple.Append module creates instances for these typeclasses.

Usually appending, sequencing and folding tuples is not a good idea. So if you use this package, it is definitely something is not done very elegantly in your own software. There might however be some rare cases where these operations are indeed useful.

Standard instances

For TupleAddL and TupleAddR the Data.Tuple.Append module defines instances for (<++) ∷ x → (v₁, v₂, …, vₙ) → (x, v₁, v₂, …, vₙ) and (++>) ∷ (v₁, v₂, …, vₙ) → x → (v₁, v₂, …, vₙ, x) for 0 ≤ n ≤ 61. These will construct n+1-tuples so it can construct at most a 62-tuple, which is the maximum number of elements that can be stored in a tuple in GHC. This thus means that there are 62 instances for the TupleAddL and TupleAddR typeclasses for tuples.

For the (+++) ∷ (u₁, u₂, …, uₘ) → (v₁, v₂, …, vₙ) → (u₁, u₂, …, uₘ, v₁, v₂, …, vₙ) function, the module defines instances for 0 ≤ m, n ≤ 19 with 0 ≤ m+n ≤ 19, it thus can append all possible tuples up to a 19-tuple. This is the maximum size that is defined for the Control.Lens.Tuple module in the lens package on tuple items. We think this is reasonable: for the constructed k-tuple, there are k+1 instances, which means that 210 instances are defined in total.

The SequenceTuple has instances for sequenceTupleA ∷ Applicative f ⇒ (f v₁, f v₂, …, f vₙ) → f (v₁, v₂, …, vₙ) and sequenceTupleA_ ∷ Applicative f ⇒ (f v₁, f v₂, …, f vₙ) → f () for 1 le; n ≤ 62. This can thus sequence any tuple that is allowed in GHC, since a 62-tuple is the largest tuple allowed.

The FoldTuple has instances for foldlTuple ∷ (a → v → a) → a -> (v₁, v₂, …, vₙ) → a, foldrTuple ∷ (v → a → a) → a -> (v₁, v₂, …, vₙ) → a, and foldMapTuple ∷ Monoid m ⇒ (a → m) → (v₁, v₂, …, vₙ) → m for 1≤n≤62. We thus can fold any non-empty tuple that is allowed in GHC, since a 62-tuple is the largest tuple allowed. Folding an empty tuple is not possible since then there the functional dependencies do not hold.

Besides tuples (<++) ∷ x → [x] → [x], (++>) ∷ [x] → x → [x], (+++) ∷ [u] → [u] → [u], sequenceTupleA ∷ Applicative f ⇒ [f u] → f [u], sequenceTupleA_ ∷ Applicative f ⇒ [f u] → f (), foldlTuple ∷ (a → v → a) → a -> (v₁, v₂, …, vₙ) → a, foldrTuple ∷ (v → a → a) → a -> (v₁, v₂, …, vₙ) → a, and foldMapTuple ∷ Monoid m ⇒ (a → m) → (v₁, v₂, …, vₙ) → m are also defined on lists and NonEmpty objects, to make the functions more reusable. One can also define these on other collections like Text, Vectors, etc. For that, one can use the tuple-append-instances package.

For builds where one makes use of ghc-prim prior to version ghc-prim-0.7.0, the Solo data type (a tuple with one element) is not available. For these builds, it will thus not make instances with singleton tuples, and thus there are only 61 instances for TupleAddL and TupleAddR and 173 instances for TupleAppend that work with tuples. Whether the "unit type" () is a tuple with no elements, and the Solo type is a tuple with exactly one element is debatable, but regardless, this package implemented instances for these.

Generating (extra) functions and instances

One can create extra functions and typeclass instances to prepend and append tuples. While we think that the number of instances for boxed tuples is likely sufficient for all practical use cases, this might be more useful when one aims to construct such functions for example for unboxed tuples, or for boxed tuples for specific types to make it more clear what the function is doing. Such unboxed types can not be specified through a type parameter. Since there are several unboxed types, exhaustively creating functions for all such types would result in millions of functions, which would likely exhaust the memory of the compiler.

One can however make use of these functions to generate such functions such that it is unlikely to make mistakes, for example by swapping two variables that have the same type. For these one can make use of the makeBoxedTupleAppendFun, makeUnboxedTupleAppendFun, makeBoxedAddLFun, makeUnboxedAddLFun, makeBoxedAddRFun, and makeUnboxedAddRFun functions. These functions can be used when working with template Haskell to generate Haskell code. See for example the following example:

{-# LANGUAGE MagicHash, TemplateHaskell, UnboxedTuples #-}
{-# OPTIONS_GHC -fobject-code #-}

module Data.Tuple.Append.Example where

import Data.Tuple.Append.TemplateHaskell(makeBoxedTupleAppendFun, makeUnboxedTupleAppendFun)

import GHC.Exts(Float#, Int#)

import Language.Haskell.TH.Syntax(Type(ConT, VarT), mkName)

makeBoxedTupleAppendFun (mkName "append_if_f") [ ConT ''Int, ConT ''Float ] [ ConT ''Float ]
makeUnboxedTupleAppendFun (mkName "uappend_ix_f") [ ConT ''Int#, VarT (mkName "a")] [ConT ''Float# ]

This will create a function named append_if_f ∷ (Int, Float) → Solo Float → (Int, Float, Float) that appends a 2-tuple with an Int and a Float to a singleton tuple with a Float to a 3-tuple with an Int, a Float and another Float. Furthermore it creates a function named uappend_ix_f ∷ (# Int#, a #) → (# Float# #) → (# Int#, a, Float# #) that will append an unboxed tuple (# Int#, a #) with an Int# a type variable a and an unboxed tuple (# Float# #) with a Float# to an unboxed tuple (# Int#, a, Float# #). This example can be found in the hidden Data.Tuple.Append.Example module.

Laziness

The prepend and append functions are implemented with irrefutable patterns for the tuples. So an append function is implemented as:

~(u₁, u₂, …, uₘ) +++ ~(v₁, v₂, …, vₙ) = (u₁, u₂, …, uₘ, v₁, v₂, …, vₙ)

These tildes will prevent evaluating the tuples to weak head normal-form (WHNF) if this is not necessary. For example if one would use:

ghci> let (_, _, x, _) = undefined +++ (4, 2) in x
4

then this will return 4, whereas if we would not use an irrefutable pattern, then this would error, since it will first try to evaluete the first and second parameters to WHNF, and if one of these fails, will thus raise an error.

This is not the case for unboxed tuples, since then the parameters will not be the result of an evaluation, but are already passed without the tuple.

Package structure

The package contains three modules:

  • Data.Tuple.Append that re-exports the TupleAddL, TupleAddR and TupleAppend typeclasses together with instances a large number of tuple types;
  • Data.Tuple.Append.Class that defines the TupleAddL, TupleAddR and TupleAppend typeclasses together with instances for lists; and
  • Data.Tuple.Append.TemplateHaskell that creates template Haskell expressions for function defintions and typeclass instances for boxed and unboxed tuples.

tuple-append is safe Haskell

The package contains a module Data.Tuple.Append.TemplateHaskell that defines routines to construct the instances for a large number of tuples. These are then used with quasiquotation to define instances in a safe module. All modules in the package are safe, except the Data.Tuple.Append.Example module, which is not exported.

Contribute

You can contribute by making a pull request on the GitHub repository.

You can contact the package maintainer by sending a mail to hapytexeu+gh@gmail.com.