| Safe Haskell | None |
|---|
Data.Shapely
- class Exponent (Normal a) => Shapely a where
- class NormalConstr t ~ (,) => Product t
- class NormalConstr e ~ Either => Sum e
- deriveShapely :: Name -> Q [Dec]
- class (Shapely a, Shapely b) => Isomorphic a b where
- coerce :: a -> b
- class (SpineOf ts, Shapely a, Shapely b) => CoercibleWith ts a b where
- coerceWith :: ts -> a -> b
- class Massageable a b where
- massage :: a -> b
- ($$) :: (Shapely a, Shapely b) => (Normal a -> Normal b) -> a -> b
Documentation
You can generate a Shapely instance for custom types with, e.g.
data L a = Snoc (L a) a | Lin deriveShapely ''L
Then you can use coerce or massage to convert between types, or see the
functions in Data.Shapely.Normal for functions for composing and transforming
Normal form types, including algebraic operations and coversion functions.
class Exponent (Normal a) => Shapely a whereSource
Instances of the Shapely class have a Normal form representation,
made up of (,), () and Either, and functions to convert from a and
back to a again.
Associated Types
A Shapely instances "normal form" representation, consisting of
nested product, sum and unit types. Types with a single constructor will
be given a Product Normal representation, where types with more than
one constructor will be Sums.
See the documentation for deriveShapely, and the instances defined here
for details.
Methods
constructorsOf :: a -> Normal a :=>-> aSource
Instances
| Shapely Bool | |
| Shapely Ordering | |
| Shapely () | |
| Shapely [a] | |
| Shapely (Maybe a0) | |
| Shapely (Either x y) | |
| Shapely (x, y) | Note, the normal form for a tuple is not itself |
| Shapely (a0, b0, c0) | |
| Shapely (a0, b0, c0, d0) | |
| Shapely (a0, b0, c0, d0, e0) | |
| Shapely (a0, b0, c0, d0, e0, f0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0) | |
| Shapely (a0, b0, c0, d0, e0, f0, g0, h0, i0, j0, k0, l0, m0, n0, o0) |
class NormalConstr t ~ (,) => Product t Source
A Product is a list of arbitrary terms constructed with (,), and
terminated by () in the snd. e.g.
prod = (1,(2,(3,())))
Deriving Shapely instances automatically
deriveShapely :: Name -> Q [Dec]Source
Generate a Shapely instance for the type passed as argument nm. Used
like:
$(deriveShapely ''Tree) -- two single-quotes reference a TH "Name"
The algorithm used here to generate the Normal instance is most easily
described syntactically:
- Constructors are replaced with
(), which terminate (rather than start) a product - Product terms are composed with nested tuples, e.g.
Foo a b c ==> (a,(b,(c,()))) - The
|in multiconstructor (Sum) type declarations is replaced withEither, with a nesting like the above
Note that a Product type in the Right place terminates a composed
Sum, while a () in the snd place terminates the composed terms
of a Product.
Typed conversion functions
class (Shapely a, Shapely b) => Isomorphic a b whereSource
Two types a and b are isomorphic, by this definition, if they have the
same number and ordering of constructors, and where Product terms are
identical or a corresponding recursive a and b.
Methods
Convert a possibly direct-recursive type a to an isomorphic type
b. This is defined:
coerce a = 'coerceWith' ('unappliedOf' a, ()) a
See massage for a more powerful and flexible conversion function
supporting direct recursion.
Instances
| (Unapplied k (Proxy * txy) t, CoercibleWith (Proxy k t, ()) txy b) => Isomorphic txy b | We use |
class (SpineOf ts, Shapely a, Shapely b) => CoercibleWith ts a b whereSource
A Shapely type a coercible to b where types in the spine ts are
recursively coerceWith-ed to b.
Methods
coerceWith :: ts -> a -> bSource
Instances
| (Shapely a, Shapely b, CoercibleNormalWith ts (Normal a) (Normal b)) => CoercibleWith ts a b |
class Massageable a b whereSource
DISCLAIMER: this function is experimental (although it should be correct) and the behavior may change in the next version, based on user feedback. Please see list of limitations below and send feedback if you have any.
A class for typed, principled, "fuzzy coercions" between types. See also
MassageableNormal.
This works as follows (or see examples below):
- All
Products in the sourceamust be mappable unambiguously to exactly one product in the targetb, according to the rules below. This is a total function, and all product terms in the source are preserved. - Products in
acome in two flavors which are mapped differently ontob: if a source product contains all uniquely-typed terms we treat it as a type-indexed product (TIP) and its terms will be freely shuffled to match its target; otherwise we consider the ordering of product terms to be significant and require a target product with the same ordering. The mapping between terms in source and target products is a bijection. - We map source product subterm
as with targetbsubterms, by recursively applyingmassage(this is the only exception to the above, and the only place where we inspectProductsubterms). - When the source
ais aSumthis conversion may be surjective wrt the product mappings, i.e. multiple source "constructors" may map to the same target constructor. But again the individual mappings must be unambiguous.
Here are some examples:
data Tsil a = Snoc (Tsil a) a | Lin
deriving Eq
deriveShapely ''Tsil
truth = massage "123" == Snoc (Snoc (Snoc Lin '3') '2') '1'
One limitation is we don't support a way to handle recursive structures
beyond top-level direct recursion (e.g. mutually-recusrive pairs of types).
And unlike coerce functor type-applied recursive terms are not supported.
Any feedback on the above behavior would be greatly appreciated.
Instances
| (Shapely a, Shapely b, MassageableNormalRec a b (Normal a) (Normal b)) => Massageable a b |