Safe Haskell | None |
---|

- 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.

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 `Sum`

s.

See the documentation for `deriveShapely`

, and the instances defined here
for details.

constructorsOf :: a -> Normal a :=>-> aSource

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 with`Either`

, 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`

.

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.

(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`

.

coerceWith :: ts -> a -> bSource

(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
`Product`

s in the source`a`

must be mappable unambiguously to exactly one product in the target`b`

, according to the rules below. This is a total function, and all product terms in the source are preserved. - Products in
`a`

come in two flavors which are mapped differently onto`b`

: 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
`a`

s with target`b`

subterms, by recursively applying`massage`

(this is the only exception to the above, and the only place where we inspect`Product`

subterms). - When the source
`a`

is a`Sum`

this conversion may be surjective w*r*t 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.

(Shapely a, Shapely b, MassageableNormalRec a b (Normal a) (Normal b)) => Massageable a b |