The reorderable package


The pair `(Int, Float)' is entirely distinct from the pair `(Float, Int)' and trying to use one in place of the other will give a type error. This is often, but not always, desired.

This module provides more flexible sum and product types that do not enforce a single order on their elements. This does introduce some necessary restrictions, for example only one instance of any type can appear in any given collection of types. Additionally, all types that are to be used in one of these flexible containers must be pre-defined as reorderable:

data MyType1 = MyType1 Int
data MyType2 = MyType2 Float
data MyType3 = MyType3 Bool
data MyType3 = MyType4 String
reorderable ''MyType1
reorderable ''MyType2
reorderable ''MyType3
reorderable ''MyType4

That will, using Template Haskell, generate all the required instances to make those types usable as reorderable types within unordered containers. Following that, all the declarations below are valid:

type Reordered1A = ReorderableEnd :*: MyType2 :*: MyType1
type Reordered1B = ReorderableEnd :*: MyType1 :*: MyType2
type Reordered2  = Reordered1A    :*: MyType3
type Reordered3  = ReorderableEnd :*: MyType4 :*: Reordered1B

Types Reordered1A and Reordered1B are in fact now identical. This does introduce a third limitation of the library I have been unable to lift - the use of ReorderableEnd as a sentinel in all reorderable containers.

It may be the case that Type1 and Type2 can be used together, as can Type3 and Type4, but the two sets of types can not be used in a container together. These are groups of types:

reorderableGroup [''MyType1, ''MyType2]
reorderableGroup [''MyType3, ''MyType4]

The groups can overlap:

reorderableGroup [''MyType1, ''MyType2]
reorderableGroup [''MyType1, ''MyType3, ''MyType4]

But this may cause some "leakage" where types from two different groups (for example MyType2 and MyType4) end up in the same container, attached via common types.

For each type X for which reorderable (or equivalent) is called, the following functions are generated (where X is the type name):

addSumX :: (x :>: s) => s -> s :+: x
setSumX :: (x :s) = x -> s -> s
getSumX :: (x :s) = s -> Maybe x
addProductX :: (x :~: s) => x -> s -> s :*: x
setProductX :: (x :?: s) => x -> s -> s
getProductX :: (x :?: s) => s -> x
removeProductX :: (x :?: s) => s -> s :-: x

In addition to being able to control for which types code is generated, you can control what code is generated for them through reorderers. Note that the default code listed above is ALWAYS generated, you can currently only ADD to the generation code. The simplest way to explain this is through an example:

class ReorderableSum a
addSum??? :: (OutSumType without ???) => without -> AddSumType without ???
addSum??? without = addSumType without (undefined :: ???)
setSum??? :: (InSumType with ???) => ??? -> with -> with
setSum??? a b = setSumType b a
getSum??? :: (InSumType with ???) => with -> Maybe ???
getSum??? with = getSumType with (undefined :: ???)

The code above is exactly the code used to generate the sum type functions documented above. The internal class names are used in place of the type operator synonyms for simplicity. ??? is used as a placeholder to be replaced by the unqualified type names from every instance of reorderable in the code. The empty class ReorderableSum is a unique name with a single type parameter, passed as the first symbol to the reorderer. An instance of this class is generated for each reorderable type, to track for which types this reorderer has already been generated (using reify). The simple reason for this is that placing the same type in two reorderableGroups will, without that check, attempt to instantiate this code twice and thus give errors.

What can be done within generators is very constrained. For one thing, the parameter a to ReorderableSum currently MUST have kind *, so any reorderable types may not have type parameters themselves (unless a new generator is written for exactly that type). Additionally, the placeholder ??? in no way accounts for complex names - it is purely a text-based replacement, so trying to create a reorderable ``Maybe Int'' type will result in the illegal:

addSumMaybe Int :: ...

Finally, this code is processed with "haskell-src-meta", and so any code must be parsable with that code. One lifting of this restriction is that reorderers may additionally contain type family declarations, which are by default not supported by that library (despite having issued a pull request many months ago).


Versions 0.3, 0.3.1
Dependencies base (>= && <5), constraints (>=0.1), haskell-src-exts (==1.14.*), haskell-src-meta (>=, template-haskell (>= [details]
License OtherLicense
Copyright (c) 2013, Alex Cole
Author Alex Cole <>
Maintainer Alex Cole <>
Stability Experimental
Category Type System, Data
Uploaded Fri Nov 15 16:42:07 UTC 2013 by AlexCole
Downloads 465 total (7 in the last 30 days)
0 []
Status Docs not available [build log]
Successful builds reported [all 7 reports]


  • Data
    • Types
      • Data.Types.Reorder
        • Data.Types.Reorder.Base
        • Data.Types.Reorder.Instances
        • Data.Types.Reorder.Product
        • Data.Types.Reorder.Quoter
        • Data.Types.Reorder.Sum
        • Data.Types.Reorder.TH


Maintainer's Corner

For package maintainers and hackage trustees