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 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
.
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 sourcea
must 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
a
come 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
a
s with targetb
subterms, by recursively applyingmassage
(this is the only exception to the above, and the only place where we inspectProduct
subterms). - When the source
a
is aSum
this 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.
(Shapely a, Shapely b, MassageableNormalRec a b (Normal a) (Normal b)) => Massageable a b |