% Extensible sum types
% [Public domain]
\input birdstyle
\birdleftrule=1pt
\emergencystretch=1em
\def\hugebreak{\penalty-600\vskip 30pt plus 8pt minus 4pt\relax}
\newcount\chapno
\def\: #1.{\advance\chapno by 1\relax\hugebreak{\bf\S\the\chapno. #1. }}
\: Introduction. This module implements extensible sum types, which means
you can do something like a datatype where you can add additional
constructors even in other modules.
>
>
> module Data.Extensible.Sum (
> ExtSum(..), ExtSumC(..), SumSelector(..), callExtSum, nextExtSum,
> castExtSum, selectExtSum, lensExtSum
> ) where {
> import Control.Applicative;
> import Control.Monad;
> import Data.Lens.Common;
> import Data.Typeable;
> import GHC.Exts (Any);
> import Unsafe.Coerce;
\: Implementation. This implementation is based on a constrained dependent
sum type; there is a tag and then the value it corresponds to which is set
up by the instances of that class.
> data ExtSum s where {
> ExtSum :: forall s x. ExtSumC s x => x -> ExtSumF x -> ExtSum s;
> } deriving Typeable;
There are no special laws that need to be satisfied with instances of this
class.
> class (Eq x, Typeable x) => ExtSumC s x | x -> s where {
> type ExtSumF x :: *;
> accessExtSum :: x -> ExtSumF x -> (s, s -> x);
> };
This type is used for selectors. A selector is used to convert a value of
one of the choices for an extensible sum into a value of a single type.
> data SumSelector s v where {
> (:+?) :: forall s x v. ExtSumC s x =>
> x -> (ExtSumF x -> v) -> SumSelector s v;
> };
> infix 0 :+?;
\: Functions.
{\tt callExtSum}: Can access the value of the single type which it
corresponds to. This type might even be an extensible product type.
> callExtSum :: ExtSum s -> s;
> callExtSum (ExtSum x y) = fst (accessExtSum x y);
{\tt nextExtSum}: Make a change in the selector. This is not generally a
functor.
> nextExtSum :: (s -> s) -> ExtSum s -> ExtSum s;
> nextExtSum f (ExtSum x y) = let { (a, b) = accessExtSum x y; } in
> ExtSum (b $ f a) y;
{\tt castExtSum}: Ask the constructor, and will make the value if that is
the one which is active.
> castExtSum :: ExtSumC s x => ExtSum s -> x -> Maybe (ExtSumF x);
> castExtSum (ExtSum x y) t = unsafeCoerce y
> <$ guard (typeOf x == typeOf t && x == unsafeCoerce t);
{\tt selectExtSum}: Given a list of selectors, take the value selected
from the extensible sum value, and apply the selector.
> selectExtSum :: [SumSelector s v] -> ExtSum s -> Maybe v;
> selectExtSum [] _ = Nothing;
> selectExtSum ((n :+? f) : t) x = (f <$> castExtSum x n)
> <|> selectExtSum t x;
{\tt lensExtSum}: Make a lens of an extensible sum type.
> lensExtSum :: Lens (ExtSum s) s;
> lensExtSum = lens callExtSum $ nextExtSum . const;
% End of document (final "}" is suppressed from printout)
\medskip\centerline{The End}
\toks0={{
> }