zipper-0.1: src/Generics/MultiRec/Zipper.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-----------------------------------------------------------------------------
-- |
-- Module : Generics.MultiRec.Zipper
-- Copyright : (c) 2008--2009 Universiteit Utrecht
-- License : BSD3
--
-- Maintainer : generics@haskell.org
-- Stability : experimental
-- Portability : non-portable
--
--
-- The generic zipper.
--
-----------------------------------------------------------------------------
module Generics.MultiRec.Zipper
(-- * Locations
Loc(),
-- * Context frames
Ctx(),
-- * Generic zipper class
Zipper(..),
-- * Interface
enter,
down, down', up, right, left,
dfnext, dfprev,
leave, on, update, foldZipper
) where
import Prelude hiding (last)
import Control.Monad
import Data.Maybe
import Generics.MultiRec.Base
import Generics.MultiRec.Fold
import Generics.MultiRec.HFunctor
import Generics.MultiRec.Zipper.TEq
-- * Locations and context stacks
-- | Abstract type of locations. A location contains the current focus
-- and its context. A location is parameterized over the system of
-- datatypes and over the type of the complete value.
data Loc :: (* -> *) -> (* -> *) -> * -> * where
Loc :: (Ix s ix, Zipper (PF s)) => r ix -> Ctxs s r a ix -> Loc s r a
data Ctxs :: (* -> *) -> (* -> *) -> * -> * -> * where
Empty :: Ctxs s r a a
Push :: Ix s ix => Ctx (PF s) s r ix b -> Ctxs s r a ix -> Ctxs s r a b
-- * Context frames
-- | Abstract type of context frames. Not required for the high-level
-- navigation functions.
data family Ctx f :: (* -> *) -> (* -> *) -> * -> * -> *
data instance Ctx (K a) s r ix b
data instance Ctx U s r ix b
data instance Ctx (f :+: g) s r ix b = CL (Ctx f s r ix b)
| CR (Ctx g s r ix b)
data instance Ctx (f :*: g) s r ix b = C1 (Ctx f s r ix b) (g s r ix)
| C2 (f s r ix) (Ctx g s r ix b)
-- The equality constraints simulate GADTs. GHC currently
-- does not allow us to use GADTs as data family instances.
data instance Ctx (I xi) s r ix b = CId (b :=: xi)
data instance Ctx (f :>: xi) s r ix b = CTag (ix :=: xi) (Ctx f s r ix b)
data instance Ctx (C c f) s r ix b = CC (Ctx f s r ix b)
-- * Generic navigation functions
-- | It is in general not necessary to use the generic navigation
-- functions directly. The functions listed in the ``Interface'' section
-- below are more user-friendly.
--
class HFunctor f => Zipper f where
cmap :: (forall b. Ix s b => s b -> r b -> r' b) ->
Ctx f s r ix b -> Ctx f s r' ix b
fill :: Ix s b => Ctx f s r ix b -> r b -> f s r ix
first, last :: (forall b. Ix s b => r b -> Ctx f s r ix b -> a)
-> f s r ix -> Maybe a
next, prev :: (forall b. Ix s b => r b -> Ctx f s r ix b -> a)
-> Ix s b => Ctx f s r ix b -> r b -> Maybe a
instance Zipper (I xi) where
cmap f (CId prf) = CId prf
fill (CId prf) x = castId prf I x
first f (I x) = return (f x (CId Refl))
last f (I x) = return (f x (CId Refl))
next f (CId prf) x = Nothing
prev f (CId prf) x = Nothing
instance Zipper (K a) where
cmap f void = impossible void
fill void x = impossible void
first f (K a) = Nothing
last f (K a) = Nothing
next f void x = impossible void
prev f void x = impossible void
instance Zipper U where
cmap f void = impossible void
fill void x = impossible void
first f U = Nothing
last f U = Nothing
next f void x = impossible void
prev f void x = impossible void
instance (Zipper f, Zipper g) => Zipper (f :+: g) where
cmap f (CL c) = CL (cmap f c)
cmap f (CR c) = CR (cmap f c)
fill (CL c) x = L (fill c x)
fill (CR c) y = R (fill c y)
first f (L x) = first (\z -> f z . CL) x
first f (R y) = first (\z -> f z . CR) y
last f (L x) = last (\z -> f z . CL) x
last f (R y) = last (\z -> f z . CR) y
next f (CL c) x = next (\z -> f z . CL) c x
next f (CR c) y = next (\z -> f z . CR) c y
prev f (CL c) x = prev (\z -> f z . CL) c x
prev f (CR c) y = prev (\z -> f z . CR) c y
instance (Zipper f, Zipper g) => Zipper (f :*: g) where
cmap f (C1 c y) = C1 (cmap f c) (hmap f y)
cmap f (C2 x c) = C2 (hmap f x) (cmap f c)
fill (C1 c y) x = fill c x :*: y
fill (C2 x c) y = x :*: fill c y
first f (x :*: y) =
first (\z c -> f z (C1 c y )) x `mplus`
first (\z c -> f z (C2 x c )) y
last f (x :*: y) =
last (\z c -> f z (C2 x c )) y `mplus`
last (\z c -> f z (C1 c y )) x
next f (C1 c y) x =
next (\z c' -> f z (C1 c' y )) c x `mplus`
first (\z c' -> f z (C2 (fill c x) c')) y
next f (C2 x c) y =
next (\z c' -> f z (C2 x c')) c y
prev f (C1 c y) x =
prev (\z c' -> f z (C1 c' y )) c x
prev f (C2 x c) y =
prev (\z c' -> f z (C2 x c')) c y `mplus`
last (\z c' -> f z (C1 c' (fill c y))) x
instance Zipper f => Zipper (f :>: xi) where
cmap f (CTag prf c) = CTag prf (cmap f c)
fill (CTag prf c) x = castTag prf Tag (fill c x)
first f (Tag x) = first (\z -> f z . CTag Refl) x
last f (Tag x) = last (\z -> f z . CTag Refl) x
next f (CTag prf c) x = next (\z -> f z . CTag prf) c x
prev f (CTag prf c) x = prev (\z -> f z . CTag prf) c x
instance (Constructor c, Zipper f) => Zipper (C c f) where
cmap f (CC c) = CC (cmap f c)
fill (CC c) x = C (fill c x)
first f (C x) = first (\z -> f z . CC) x
last f (C x) = last (\z -> f z . CC) x
next f (CC c) x = next (\z -> f z . CC) c x
prev f (CC c) x = prev (\z -> f z . CC) c x
-- * Interface
-- ** Introduction
-- | Start navigating a datastructure. Returns a location that
-- focuses the entire value and has an empty context.
enter :: (Ix s ix, Zipper (PF s)) => s ix -> ix -> Loc s I0 ix
enter _ x = Loc (I0 x) Empty
-- ** Navigation
-- | Move down to the leftmost child. Returns 'Nothing' if the
-- current focus is a leaf.
down :: Loc s I0 ix -> Maybe (Loc s I0 ix)
-- | Move down to the rightmost child. Returns 'Nothing' if the
-- current focus is a leaf.
down' :: Loc s I0 ix -> Maybe (Loc s I0 ix)
-- | Move up to the parent. Returns 'Nothing' if the current
-- focus is the root.
up :: Loc s I0 ix -> Maybe (Loc s I0 ix)
-- | Move to the right sibling. Returns 'Nothing' if the current
-- focus is the rightmost sibling.
right :: Loc s r ix -> Maybe (Loc s r ix)
-- | Move to the left sibling. Returns 'Nothing' if the current
-- focus is the leftmost sibling.
left :: Loc s r ix -> Maybe (Loc s r ix)
down (Loc (I0 x) s ) = first (\z c -> Loc z (Push c s)) (from x)
down' (Loc (I0 x) s ) = last (\z c -> Loc z (Push c s)) (from x)
up (Loc x Empty ) = Nothing
up (Loc x (Push c s)) = return (Loc (I0 $ to (fill c x)) s)
right (Loc x Empty ) = Nothing
right (Loc x (Push c s)) = next (\z c' -> Loc z (Push c' s)) c x
left (Loc x Empty ) = Nothing
left (Loc x (Push c s)) = prev (\z c' -> Loc z (Push c' s)) c x
-- ** Derived navigation.
df :: (a -> Maybe a) -> (a -> Maybe a) -> (a -> Maybe a) -> a -> Maybe a
df d u lr l =
case d l of
Nothing -> df' l
r -> r
where
df' l =
case lr l of
Nothing -> case u l of
Nothing -> Nothing
Just l' -> df' l'
r -> r
-- | Move through all positions in depth-first left-to-right order.
dfnext :: Loc s I0 ix -> Maybe (Loc s I0 ix)
dfnext = df down up right
-- | Move through all positions in depth-first right-to-left order.
dfprev :: Loc s I0 ix -> Maybe (Loc s I0 ix)
dfprev = df down' up left
-- ** Elimination
-- | Return the entire value, independent of the current focus.
leave :: Loc s I0 ix -> ix
leave (Loc (I0 x) Empty) = x
leave loc = leave (fromJust (up loc))
-- | Operate on the current focus. This function can be used to
-- extract the current point of focus.
on :: (forall xi. Ix s xi => s xi -> r xi -> a) -> Loc s r ix -> a
on f (Loc x _) = f index x
-- | Update the current focus without changing its type.
update :: (forall xi. Ix s xi => s xi -> xi -> xi) -> Loc s I0 ix -> Loc s I0 ix
update f (Loc (I0 x) s) = Loc (I0 $ f index x) s
-- | Most general eliminator. Both 'on' and 'update' can be defined
-- in terms of 'foldZipper'.
foldZipper :: (forall xi. Ix s xi => s xi -> xi -> r xi) -> Algebra s r -> Loc s I0 ix -> r ix
foldZipper f alg (Loc (I0 x) c) = cfold alg c (f index x)
where
cfold :: (Ix s b, Zipper (PF s)) => Algebra s r -> Ctxs s I0 a b -> r b -> r a
cfold alg Empty x = x
cfold alg (Push c s) x = cfold alg s (alg index (fill (cmap (\ _ (I0 x) -> fold alg x) c) x))
-- * Internal functions
impossible :: a -> b
impossible x = error "impossible"
-- Helping the typechecker to apply equality proofs correctly ...
castId :: (b :=: xi)
-> (Ix s xi => r xi -> I xi s r ix)
-> (Ix s b => r b -> I xi s r ix)
castTag :: (ix :=: xi)
-> (f s r ix -> (f :>: ix) s r ix)
-> (f s r ix -> (f :>: xi) s r ix)
castId Refl f = f
castTag Refl f = f