bot-0.3: src/Data/Bot/Part.hs
{-# LANGUAGE MagicHash, GADTs, KindSignatures, ScopedTypeVariables
, Rank2Types #-}
{-# OPTIONS_GHC -Wall #-}
----------------------------------------------------------------------
-- |
-- Module : Data.Bot.Part
-- Copyright : (c) Conal Elliott 2008
-- License : BSD3
--
-- Maintainer : conal@conal.net
-- Stability : experimental
--
--
----------------------------------------------------------------------
module Data.Bot.Part where
import Control.Arrow hiding (pure)
import Control.Applicative
import Data.Maybe (fromMaybe)
import GHC.Base (unsafeCoerce#) -- used in partMatch and coerceV
-- | Part of a structure of nested pairs
data Part :: * -> * -> * where
IdP :: Part a a
FirstP :: Part c a -> Part (c,b) a
SecondP :: Part c b -> Part (a,c) b
-- | Equality on parts
partEq :: Part i a -> Part i b -> Bool
IdP `partEq` IdP = True
FirstP p `partEq` FirstP q = p `partEq` q
SecondP p `partEq` SecondP q = p `partEq` q
_ `partEq` _ = False
-- | Leibniz equality
type Leibniz a a' = forall phi. phi a -> phi a'
--newtype Leibniz a a' = Leibniz (forall phi. phi a -> phi a')
-- | Try to match parts. If successful, yield a proof of Leibniz
-- equality, i.e., a coercion.
partMatch :: Part c a -> Part c a' -> Maybe (Leibniz a a')
a `partMatch` a'
| a `partEq` a' = Just unsafeCoerce#
| otherwise = Nothing
-- | Extract part of a value. Use staged to avoid interpretive overhead.
extract :: Part c a -> (c -> a)
extract IdP = id
extract (FirstP p) = extract p . fst
extract (SecondP p) = extract p . snd
-- | Change a part of a value. Use staged to avoid interpretive overhead.
change :: Part c a -> ((a -> a) -> (c -> c))
change IdP = id
change (FirstP p) = first . change p
change (SecondP p) = second . change p
-- I really only wanted constant @a@ values for 'change', but I like how the
-- definition mirrors @comp@'s in this slightly more general setting.
-- Also, 'extract' and 'change' relate 'Part' to composable references.
-- | Substitute for part of a value. Use staged to avoid interpretive overhead.
subst :: Part c a -> (a -> (c -> c))
subst p = change p . const
-- About the staging comments:
--
-- Because of the way I've defined 'extract', 'change', and 'subst',
-- the "syntactic" analysis of parts is done once rather than at each
-- substitution. The extra parentheses in the type signature serve as
-- a reminder. Will haddock remove the extra parens?
-- | Compose parts. Specification:
--
-- @extract (comp p q) == extract q . extract p@
-- @change (comp p q) == change p . change q@
comp :: Part c b -> (Part b a -> Part c a)
comp IdP = id
comp (FirstP p) = FirstP . comp p
comp (SecondP p) = SecondP . comp p
-- comp IdP q = q
-- comp (FirstP p) q = FirstP (comp p q)
-- comp (SecondP p) q = SecondP (comp p q)
-- extract (comp (FirstP p) q)
-- == extract q . extract (FirstP p)
-- == extract q . extract p . fst
-- == extract (FirstP (comp p q))
-- change p ::(b -> b) -> (c -> c)
-- change q ::(a -> a) -> (b -> b)
-- change p . change q :: (a -> a) -> (c -> c)
-- change (comp (FirstP p) q)
-- == change (FirstP p) . change q
-- == first . change p . change q
-- == first . change (comp p q)
-- == change (FirstP (comp p q))
---- experiments
-- \ ((a,b),c) -> a*b + b*c
t1 :: ((Int,Int),Int) -> Int
t1 = liftA2 (+)
(liftA2 (*) (fst>>>fst) (fst>>>snd))
(liftA2 (*) (fst>>>snd) snd)
-- Bot with top-level data
data DBot i o = DBot o (Bot i o)
-- Bot without top-level data
data Bot :: * -> * -> * where
PureB :: Bot i o
AppB :: DBot i (a -> b) -> DBot i a -> Bot i b
PartB :: Part i o -> Bot i o
instance Functor (DBot i) where
fmap f = (pure f <*> )
instance Applicative (DBot i) where
pure o = DBot o PureB
fd@(DBot f _) <*> xd@(DBot x _) = DBot (f x) (fd `AppB` xd)
-- TODO: factor the Applicative pattern from Bot & DBot. Parameterize
-- by @Part i@. The the general version has just one type argument.
-- | An 'Applicative'-style tree of values.
data AppV :: * -> * where
PureV :: a -> AppV a
AppV :: a -> AppV (b -> a) -> AppV b -> AppV a
-- Extract the top-level value from a tree.
vVal :: AppV a -> a
vVal (PureV a) = a
vVal (AppV a _ _) = a
appV :: AppV (b -> a) -> AppV b -> AppV a
fv `appV` bv = AppV ((vVal fv) (vVal bv)) fv bv
instance Functor AppV where
fmap f = (pure f <*> )
instance Applicative AppV where
pure = PureV
(<*>) = appV
data AppF :: (* -> *) -> * -> * where
PureF :: a -> AppF h a
AppF :: AppF h (b -> a) -> AppF h b -> AppF h a
InF :: h a -> AppF h a
instance Functor (AppF h) where
fmap f = (pure f <*> )
instance Applicative (AppF h) where
pure = PureF
(<*>) = AppF
-- | Adaptive bot.
type Adapt i = AppF (Part i)
-- | Evaluate a Adapt at an input, yielding a caching structure that
-- matches the Adapt's shape.
eval :: Adapt i o -> i -> AppV o
eval (PureF a) = const (PureV a)
eval (AppF fbot bbot) = liftA2 appV (eval fbot) (eval bbot)
eval (InF p) = PureV . extract p
-- A possible means of substituting in an @a@ value. A 'Nothing' means
-- that the substitution wouldn't make any change.
type MbSubst a b = Maybe (a -> AppV b -> AppV b)
-- | Make an update function for a bot. To eliminate interpretive
-- overhead, apply 'update' in stages: first provide a bot. Then each
-- part in turn.
update :: Adapt i o -> (Part i a -> MbSubst a o)
update (PureF _) = const Nothing
update (InF p) = \ q ->
case q `partMatch` p of
Just coerce -> Just (\ a _ -> coerce (PureV a))
Nothing -> Nothing
update (fun `AppF` arg) = \ q ->
case (msubf q, msuba q) of
(Nothing,Nothing) -> Nothing
(repf,repb) -> Just $ \ a (AppV _ vf vb) ->
upd fun repf a vf `appV` upd arg repb a vb
where
msubf = update fun
msuba = update arg
-- Apply a 'MbSubst' to a value and an 'AppV' that matches the given
-- 'AppF'. Warning: this matching requirement is not statically checked,
-- and Very Bad Things can happen if it's violated. See 'coerceV'.
upd :: AppF f b -> MbSubst a b -> a -> AppV b' -> AppV b
upd appf rep a v' = fromMaybe ((const.const) v) rep a v
where
v = coerceV appf v'
-- The 'eval' and 'update' functions maintain an invariant that
-- corresponding 'AppF' and 'AppV' have the same intermediate @b@ type.
-- This coercion function exploits that invariant.
--
-- TODO: look for a statically checked alternative.
coerceV :: AppF f b -> AppV b' -> AppV b
coerceV = const unsafeCoerce#