reorderable: Define compound types that do not depend on member order.
Introduction. 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.
Module. 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
TypesReordered1A
andReordered1B
are in fact now identical. This does introduce a third limitation of the library I have been unable to lift - the use ofReorderableEnd
as a sentinel in all reorderable containers. It may be the case thatType1
andType2
can be used together, as canType3
andType4
, 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 exampleMyType2
andMyType4
) end up in the same container, attached via common types.Generation. For each type
X
for whichreorderable
(or equivalent) is called, the following functions are generated (whereX
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
* Notes on the syntax:
:<:
Is read as "Is member of sum type".:>:
Is read as "Is not member of sum type".:+:
Is read as "Plus".:?:
Is read as "Is member of product type".:~:
Is read as "Is not member of product type".:*:
Is read as "Product".:-:
Is read as "Remove".* Notes on the functions:
addSumX
Adds the TYPEx
to the given signature, and correctly re-wraps the contained data to reflect this new structure. It does not add any data in to the structure itself because only one item may exist in the structure, and that item is already there.setSumX
Changes what data is currently stored in the sum. For a given concrete sum typeS
, this can be called as: `setSumX x (undefined :: S)'. An alternative version is simply: `setSumType (undefined :: S) x', in which `X :<: S'. This is equivalent to the originalinj
function from `Data Types 'a la Carte', but has an explicit type proxy.getSumX
Returns the data of type `Just X' IF it is the data currently being stored within the sum, otherwise it returnsNothing
. This is equivalent to the originalprj
function from `Data Types 'a la Carte'.addProductX
Adds data of typeX
to an existing product type that does not yet contain any data of that type.setProductX
Sets the data of typeX
in a product type that already contains data of that type.getProductX
Gets the data of typeX
from a product type that contains data of that type.removeProductX
Removes data of typeX
from a product type that contains data of that type, and rewraps the resulting information to removeX
from the product's type. There is noremoveSumX
function because the result is empty if the stored data is not of the type being removed.Generators. In addition to being able to control for which types code is generated, you can control what code is generated for them through
reorderer
s. 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 [reorderer|ReorderableSum 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 ofreorderable
in the code. The empty classReorderableSum
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 (usingreify
). The simple reason for this is that placing the same type in tworeorderableGroup
s 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 parametera
toReorderableSum
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).
Modules
- 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
- Data.Types.Reorder
- Types
Downloads
- reorderable-0.3.tar.gz [browse] (Cabal source package)
- Package description (as included in the package)
Maintainer's Corner
For package maintainers and hackage trustees
Candidates
- No Candidates
Versions [RSS] | 0.3, 0.3.1 |
---|---|
Dependencies | base (>=4.0.0.0 && <5), constraints (>=0.1), haskell-src-exts (>=1.14 && <1.15), haskell-src-meta (>=0.6.0.4), template-haskell (>=2.4.0.0) [details] |
License | LicenseRef-OtherLicense |
Copyright | (c) 2013, Alex Cole |
Author | Alex Cole <haskell@y-less.com> |
Maintainer | Alex Cole <haskell@y-less.com> |
Category | Type System, Data |
Uploaded | by AlexCole at 2013-11-15T16:34:11Z |
Distributions | |
Reverse Dependencies | 1 direct, 0 indirect [details] |
Downloads | 1879 total (4 in the last 30 days) |
Rating | (no votes yet) [estimated by Bayesian average] |
Your Rating | |
Status | Docs not available [build log] Successful builds reported [all 8 reports] |