packages feed

bind-marshal-0.1: test/verify_bind_rules_proto_2.hs

-- Copyright   :  (C) 2009 Corey O'Connor
-- License     :  BSD-style (see the file LICENSE)

{-# OPTIONS_GHC -fglasgow-exts #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
import Bind.Marshal.Prelude
import Bind.Marshal.Verify

import Data.Monoid

import Prelude ( error )

import Verify

-- we have a few non-parameterize functor types
data Foo a
data Bar a

-- We have an action with an embedded data model.
-- This can only have static actions appended to either end.
data Static m m' a

-- A static memory actions can be embedded in a dynamic memory action have their data models reified to values.
--
-- Currently the Dyn data type specifies a list of static action ops delineated by "buffering"
-- ops. The actual data type would contain something isomorphic, in addition to the CPS evaluators.
--
data Dyn pre_s post_s_m post_s_m' a = Dyn [Op]

data Op = SB Int
    deriving (Show, Eq)

-- and two Peano number like type equations.
data SZ
data Succ tag n

-- simple reification to integers just for verification
class ReifyM m where
    reify_m :: m -> Int

instance ReifyM SZ where
    reify_m _ = 0

instance ReifyM pred => ReifyM (Succ Tag pred) where
    reify_m _ = 1 + reify_m (undefined :: pred)

data Tag

-- and we have equations on the non-parameterized functor types
foo :: Foo ()
foo = undefined

bar :: Bar ()
bar = undefined

-- incr_m ONLY applies to Static
-- The previous formulation had this apply to both Static and Dyn and the goal was to coerce the
-- compiler into choosing the most appropriate one during type inference. This, I think, is
-- currently impossible. For simple cases the pft type of incr_m could not default to Static when
-- necessary. The type ended up being ambiguous and this resulted in the type inference engine
-- unable to resolve the Bind instances required. While Bing and Zab can introduce a type constraint
-- that fixed the pft type of incr_m. An individual incr_m could not default the pft type.
--
incr_m :: Static (Succ Tag m) m ()
incr_m = undefined

-- A "fixed" dyn block is a Dyn where the pre model is SZ (Meaning that there is no pre model) and
-- the post model is empty.
class FixedAction (action :: * -> *) where
    fixed_block :: action a -> Dyn SZ post_m_tail post_m_tail a

-- A static block can lifted to a "fixed" Dyn by reifying the static's model
instance ( m_tail ~ SZ, ReifyM m ) => FixedAction (Static m m_tail) where
    fixed_block _ = 
        let block_size = reify_m (undefined :: m)
        in Dyn [SB block_size]

-- A dynamic block can be lifted to a "fixed" Dyn by capping the post_m_tail with SZ and then
-- reifying the pre_m model.
instance ( post_m_tail ~ SZ, ReifyM pre_m ) => FixedAction (Dyn pre_m post_m post_m_tail) where
    -- The cases where pre_count or post_count are 0 can actually be handled on the type level.
    -- However the fixing of dynamic action always implies buffer handling. So the optimization of
    -- elminating some case handling is tiny compared to the cost of the required buffer handling.
    -- Therefor I currently do not think the optimization is worth it. Especially since this would
    -- require a type-level dispatch via context constraints into the following cases:
    --  no pre and no post
    --  no pre and post
    --  pre and no post
    --  pre and post
    fixed_block (Dyn mid_ops) = 
        let pre_count = reify_m (undefined :: pre_m)
            pre_ops = case pre_count of
                        0 -> []
                        i -> [SB i]
            mid_ops' = pre_ops `mappend` mid_ops
        in Dyn mid_ops'

assert_ops_are :: forall pre_m post_m . ( ReifyM pre_m, ReifyM post_m ) => [Op] -> Dyn pre_m post_m SZ () -> Test ()
assert_ops_are required_ops (Dyn tail_ops) = do
    let pre_count = reify_m (undefined :: pre_m)
    let pre_ops = case pre_count of
                    0 -> []
                    i -> [SB i]
    let actual_ops = pre_ops `mappend` tail_ops
    verify1 ("expected ops " ++ show required_ops ++ " equals actual " ++ show actual_ops) $ required_ops == actual_ops
    returnM () :: Test ()

-- and equations that are only valid on one of the specific cases of a pft paired with a proper m
-- and m'
only_static :: Static m' SZ () -> Test ()
only_static = undefined

only_dynamic :: Dyn pre_m post_m SZ () -> Test ()
only_dynamic = undefined

-- Minimally satisfactory cases to verify:
--
--  DONE single_static
--  DONE single_dyn
--  Zab'd single_dyn
--
--  single_static `bind` single_static
--  multi_static `bind` single_static
--  single_static `bind` multi_static
--  multi_static `bind` multi_static
--
--  single_dyn `bind` single_dyn
--  single_dyn `bind` multi_dyn
--  multi_dyn `bind` single_dyn
--  multi_dyn `bind` multi_dyn
--
--  single_dyn `bind` single_static
--  zab'd single_dyn `bind` single_static
--  single_dyn `bind` multi_static
--  zab'd single_dyn `bind` multi_static
--  multi_dyn `bind` single_static
--  multi_dyn `bind` multi_static
-- 
--  single_static `bind` single_dyn 
--  single_static `bind` zab'd single_dyn 
--  multi_static `bind` single_dyn 
--  single_static `bind` multi_dyn 
--  multi_static `bind` multi_dyn 
--
-- Just placing the types that should be inferred is insufficient as that permits qualified types
-- being instantiated with the provided types and trivally type check.
-- 
-- An assert is required that verifies the type is exactly a given type.
-- THis is tricky.. I think that a type class where the only instance is the desired type then
-- adding that type class as a constraint on the inferred type will work.
--
-- ???: In order to validate the type class constraint the type will have to be inferred.

-- These should work as expected
t_0 :: Test ()
t_0 = force_0 incr_m

force_0 :: T0A0 m => Static m SZ () -> Test () 
force_0 _ = returnM ()

class T0A0 x
instance T0A0 (Succ Tag SZ)

t_1 :: Test ()
t_1 = force_1 (fixed_block incr_m)

force_1 :: ( T1A0 pre_m, T1A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test () 
force_1 = assert_ops_are [SB 1]

class T1A0 m
instance T1A0 SZ

class T1A1 m
instance T1A1 m

-- Now there is a third equation that can operate on ALL of these types
-- where one of the types can determine the relationship each of the types must have to each other.
class CanBind f_0 f_1 f_2 | f_0 f_1 -> f_2 where
    bind :: f_0 a -> f_1 b -> f_2 b
    bind = undefined

instance ( m_0 ~ m_4
         , m_1 ~ m_2
         , m_3 ~ m_5
         ) =>
         CanBind (Static m_0 m_1) (Static m_2 m_3) (Static m_4 m_5)

instance ( m_1 ~ pre_m
         , pre_m' ~ m_0
         ) =>
         CanBind (Static m_0 m_1) (Dyn pre_m post_m post_m_final) (Dyn pre_m' post_m post_m_final)
    where
    bind _ (Dyn ops_rhs) = Dyn ops_rhs

instance ( post_m_final_0 ~ pre_m_1
         , pre_m_2 ~ pre_m_0
         , post_m_2 ~ post_m_1
         , post_m_final_2 ~ post_m_final_1
         ) => CanBind (Dyn pre_m_0 post_m_0 post_m_final_0) 
                      (Dyn pre_m_1 post_m_1 post_m_final_1) 
                      (Dyn pre_m_2 post_m_2 post_m_final_2)
    where
    bind (Dyn ops_lhs) (Dyn ops_rhs) = Dyn (ops_lhs `mappend` ops_rhs)

-- necessary to insert buffering for post_m_2
instance ( pre_m_2 ~ pre_m_0
         , post_m_2 ~ m_2
         , post_m_final_2 ~ m_3
         , post_m_final_0 ~ SZ
         , ReifyM post_m_2
         ) => CanBind (Dyn pre_m_0 post_m_final_0 post_m_final_0) 
                      (Static m_2 m_3)
                      (Dyn pre_m_2 post_m_2 post_m_final_2)
    where
    bind (Dyn ops_pre) _ = 
        let post_block_count = reify_m ( undefined :: post_m_2 )
            ops_post = [SB post_block_count]
        in Dyn (ops_pre `mappend` ops_post)

instance ( pre_m_2 ~ pre_m_0
         , post_m_2 ~ (Succ tag post_m_0')
         , post_m_final_0 ~ m_2
         , post_m_final_2 ~ m_3
         ) => CanBind (Dyn pre_m_0 (Succ tag post_m_0') post_m_final_0) 
                      (Static m_2 m_3)
                      (Dyn pre_m_2 post_m_2 post_m_final_2)
    where
    bind (Dyn ops_pre) _ = Dyn ops_pre

-- We have a type class that all non-parameterized functor types belong
-- that has a non-parameterized variant of bind.
--
-- however this is all defined external to this library!
class Zab (m :: * -> *) where
    zab_bind :: m a -> m b -> m b
    zab_bind = undefined

instance Zab Foo
instance Zab Bar

instance Zab (Dyn SZ post_m_tail post_m_tail)

-- a simple equation that introducs the Zab constraint
zab :: Zab m => m () -> m ()
zab = id

t_2 :: Test ()
t_2 = only_static_2 t_2_sub

t_2_sub = bind incr_m incr_m

only_static_2 :: T2A0 m => Static m SZ () -> Test ()
only_static_2 _ = returnM () :: Test ()

class T2A0 x
instance T2A0 (Succ Tag (Succ Tag SZ))

-- we require a manual lifting of the Static to a Dyn.
t_3 :: Test ()
t_3 = force_3 t_3_sub

t_3_sub = zab (fixed_block incr_m)

force_3 :: ( T3A0 pre_m, T3A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_3 = assert_ops_are [SB 1]

class T3A0 m
instance T3A0 SZ

class T3A1 m
instance T3A1 SZ

t_4 :: Test ()
t_4 = force_4 t_4_sub

t_4_sub = bind incr_m (zab (fixed_block incr_m))

force_4 :: ( T4A0 pre_m, T4A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_4 = assert_ops_are [SB 1, SB 1]

class T4A0 m
instance T4A0 (Succ Tag SZ)

class T4A1 m
instance T4A1 SZ

t_5 :: Test ()
t_5 = force_5 t_5_sub

t_5_sub = bind (zab $ fixed_block incr_m) (fixed_block incr_m)

force_5 :: ( T5A0 pre_m, T5A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_5 = assert_ops_are [SB 1, SB 1]

class T5A0 m
instance T5A0 SZ

class T5A1 m
instance T5A1 SZ

t_6 :: Test ()
t_6 = force_6 t_6_sub

-- once again, the sole incr_m must be enclosed in a fixed_block
t_6_sub = bind (zab $ fixed_block $ incr_m) (bind (bind incr_m incr_m) incr_m)

force_6 :: ( T6A0 pre_m, T6A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_6 = assert_ops_are [SB 1, SB 3]

class T6A0 m
instance T6A0 SZ

class T6A1 m
instance T6A1 (Succ Tag (Succ Tag (Succ Tag SZ)))

t_7 :: Test ()
t_7 = force_7 t_7_sub

t_7_sub = (zab (fixed_block incr_m) `bind` (fixed_block incr_m)) `bind` (bind incr_m incr_m)

force_7 :: ( T7A0 pre_m, T7A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_7 = assert_ops_are [SB 1, SB 1, SB 2]

t_7_alt :: Test ()
t_7_alt = force_7_alt t_7_alt_sub

t_7_alt_sub = zab (fixed_block incr_m) `bind` (fixed_block incr_m `bind` (bind incr_m incr_m))

force_7_alt :: ( T7A0 pre_m, T7A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_7_alt = assert_ops_are [SB 1, SB 1, SB 2]

class T7A0 m
instance T7A0 SZ

class T7A1 m
instance T7A1 (Succ Tag (Succ Tag SZ))

t_8 :: Test ()
t_8 = force_8 t_8_sub

t_8_sub = (bind incr_m incr_m) `bind` (bind incr_m incr_m)

force_8 :: T8A0 m => Static m SZ () -> Test ()
force_8 _ = returnM ()

class T8A0 m
instance T8A0 (Succ Tag (Succ Tag (Succ Tag (Succ Tag SZ))))

t_9 :: Test ()
t_9 = force_9 t_9_sub

t_9_sub = (bind incr_m incr_m) `bind` incr_m

force_9 :: ( T9A0 m, ReifyM m ) => Static m SZ () -> Test ()
force_9 _ = returnM ()

class T9A0 x
instance T9A0 (Succ Tag (Succ Tag (Succ Tag SZ)))

t_10 :: Test ()
t_10 = force_10 t_10_sub

t_10_sub = (zab $ fixed_block $ incr_m) `bind` (zab $ fixed_block $ incr_m)

force_10 :: ( T10A0 pre_m, T10A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_10 = assert_ops_are [SB 1, SB 1]

class T10A0 m
instance T10A0 SZ

class T10A1 m
instance T10A1 SZ

t_11 :: Test ()
t_11 = force_11 t_11_sub

t_11_sub = ((zab $ fixed_block $ incr_m) `bind` (zab $ fixed_block $ incr_m)) 
                `bind` (zab $ fixed_block $ incr_m)

force_11 :: ( T11A0 pre_m, T11A0 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_11 = assert_ops_are [SB 1, SB 1, SB 1]

class T11A0 m
instance T11A0 SZ

class T11A1 m
instance T11A1 SZ

t_12 :: Test ()
t_12 = force_12 t_12_sub

t_12_sub = (zab $ fixed_block $ incr_m) `bind` ((zab $ fixed_block $ incr_m) `bind` (zab $ fixed_block $ incr_m))

force_12 :: ( T12A0 pre_m, T12A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_12 = assert_ops_are [SB 1, SB 1, SB 1]

class T12A0 m
instance T12A0 SZ

class T12A1 m
instance T12A1 SZ

t_13 :: Test ()
t_13 = force_13 t_13_sub

t_13_sub = ((zab $ fixed_block $ incr_m) `bind` (zab $ fixed_block $ incr_m))  `bind` ((zab $ fixed_block $ incr_m) `bind` (zab $ fixed_block $ incr_m))

force_13 :: ( T13A0 pre_m, T13A1 post_m, ReifyM pre_m, ReifyM post_m )  => Dyn pre_m post_m SZ () -> Test ()
force_13 = assert_ops_are [SB 1, SB 1, SB 1, SB 1]

class T13A0 m
instance T13A0 SZ

class T13A1 m
instance T13A1 SZ

t_14 :: Test ()
t_14 = force_14 t_14_sub

t_14_sub = incr_m `bind` (incr_m `bind` incr_m)

force_14 :: T14A0 m => Static m SZ () -> Test ()
force_14 _ = returnM ()

class T14A0 m
instance T14A0 (Succ Tag (Succ Tag (Succ Tag SZ)))

t_15 :: Test ()
t_15 = force_15 t_15_sub

t_15_sub = (zab $ fixed_block incr_m) `bind` incr_m

force_15 :: ( T15A0 pre_m, T15A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_15 = assert_ops_are [SB 1, SB 1]

class T15A0 m
instance T15A0 SZ

class T15A1 m
instance T15A1 (Succ Tag SZ)

t_16 :: Test ()
t_16 = force_16 t_16_sub

t_16_sub = (zab $ fixed_block incr_m) `bind` (incr_m `bind` incr_m)

force_16 :: ( T16A0 pre_m, T16A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_16 = assert_ops_are [SB 1, SB 2]

class T16A0 m
instance T16A0 SZ

class T16A1 m
instance T16A1 (Succ Tag (Succ Tag SZ))

t_17 :: Test ()
t_17 = force_17 t_17_sub

t_17_sub = ((zab $ fixed_block incr_m) `bind` (zab $ fixed_block incr_m)) `bind` incr_m

force_17 :: ( T17A0 pre_m, T17A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_17 = assert_ops_are [SB 1, SB 1, SB 1]

class T17A0 m
instance T17A0 SZ

class T17A1 m
instance T17A1 (Succ Tag SZ)

t_18 :: Test ()
t_18 = force_18 t_18_sub

t_18_sub = ((zab $ fixed_block incr_m) `bind` (zab $ fixed_block incr_m)) `bind` (incr_m `bind` incr_m)

force_18 :: ( T18A0 pre_m, T18A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_18 = assert_ops_are [SB 1, SB 1, SB 2]

class T18A0 m
instance T18A0 SZ

class T18A1 m
instance T18A1 (Succ Tag (Succ Tag SZ))

t_19 :: Test ()
t_19 = force_19 t_19_sub

t_19_sub = incr_m `bind` (zab $ fixed_block incr_m)

force_19 :: ( T19A0 pre_m, T19A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_19 = assert_ops_are [SB 1, SB 1]

class T19A0 m
instance T19A0 (Succ Tag SZ)

class T19A1 m
instance T19A1 SZ

t_20 :: Test ()
t_20 = force_20 t_20_sub

t_20_sub = incr_m `bind` ((zab $ fixed_block incr_m) `bind` (zab $ fixed_block incr_m))

force_20 :: ( T20A0 pre_m , T20A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_20 = assert_ops_are [SB 1, SB 1, SB 1]

class T20A0 m
instance T20A0 (Succ Tag SZ)

class T20A1 m
instance T20A1 SZ

t_21 :: Test ()
t_21 = force_21 t_21_sub

t_21_sub = (incr_m `bind` incr_m) `bind` ((zab $ fixed_block incr_m) `bind` (zab $ fixed_block incr_m))

force_21 :: ( T21A0 pre_m, T21A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_21 = assert_ops_are [SB 2, SB 1, SB 1]

class T21A0 m
instance T21A0 (Succ Tag (Succ Tag SZ))

class T21A1 m
instance T21A1 SZ

t_22 :: Test ()
t_22 = force_22 t_22_sub

t_22_sub = (incr_m `bind` incr_m) 
    `bind` ((zab $ fixed_block incr_m) 
            `bind` incr_m
            `bind` incr_m
            `bind` incr_m
            `bind` incr_m
            `bind` (zab $ fixed_block incr_m)
            `bind` incr_m
            `bind` (zab $ fixed_block incr_m)
            `bind` incr_m
            `bind` incr_m
            `bind` (zab $ fixed_block incr_m)
            `bind` incr_m
            `bind` incr_m
            `bind` incr_m
           )

force_22 :: ( T22A0 pre_m, T22A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_22 = assert_ops_are [ SB 2
                          , SB 1
                          , SB 4
                          , SB 1
                          , SB 1
                          , SB 1
                          , SB 2
                          , SB 1
                          , SB 3
                          ]

class T22A0 m
instance T22A0 (Succ Tag (Succ Tag SZ))

class T22A1 m
instance T22A1 (Succ Tag (Succ Tag (Succ Tag SZ)))

t_23 :: Test ()
t_23 = force_23 t_23_sub

t_23_sub = (incr_m `bind` incr_m) 
    `bind` (zab ( fixed_block (incr_m `bind` zab (fixed_block incr_m)) ) 
           )

force_23 :: ( T23A0 pre_m, T23A1 post_m, ReifyM pre_m, ReifyM post_m ) => Dyn pre_m post_m SZ () -> Test ()
force_23 = assert_ops_are [ SB 2
                          , SB 1
                          , SB 1
                          ]

class T23A0 m
instance T23A0 (Succ Tag (Succ Tag SZ))

class T23A1 m
instance T23A1 SZ

main = run_test $ do
    t_0
    t_1
    t_2
    t_3
    t_4
    t_5
    t_6
    t_7
    t_7_alt
    t_8
    t_9
    t_10
    t_11
    t_12
    t_13
    t_14
    t_15
    t_16
    t_17
    t_18
    t_19
    t_20
    t_21
    t_22
    t_23
    returnM () :: Test ()