packages feed

state-bag-0.1.0.0: test/Main.hs

{-# LANGUAGE ScopedTypeVariables, DataKinds, TypeFamilies, RankNTypes #-}

module Main where

import Test.Hspec
import Data.Functor.Identity
import Control.Monad.Trans.StateBag.Class
import Control.Monad.ST
import qualified Control.Monad.Trans.StateBag.Pure as Pure
import qualified Control.Monad.Trans.StateBag.Primitive as Prim

main :: IO ()
main = hspec $ do
  let abc0 = (1,2,3)
      abc1 = (4,5,6)
      abcF = ((+10), (+20), (+30))
      abc2 = (11,22,33)
      check r (a, b) = r == a && r == b
  describe "Pure.StateBag" $ do
      it "can get" $
          check abc0 $ runPureBag abc0 getABC
      it "can put" $
          check abc1 $ runPureBag abc0 $ putABC abc1 >> getABC
      it "can modify" $
          check abc2 $ runPureBag abc0 $ modifyABC abcF >> getABC
  describe "Prim.StateBag" $ do
      it "can get" $
          check abc0 $ runPrimBag abc0 getABC
      it "can put" $
          check abc1 $ runPrimBag abc0 $ putABC abc1 >> getABC
      it "can modify" $
          check abc2 $ runPrimBag abc0 $ modifyABC abcF >> getABC

newtype TypeA = TypeA Int
newtype TypeB = TypeB Int
newtype TypeC = TypeC Int

runPrimBag ::
    (Int, Int, Int) ->
    (forall s. Prim.StateBagT '[TypeA, TypeB, TypeC] (ST s) a) ->
    ((Int, Int, Int), a)
runPrimBag (a, b, c) m =
  runST $
  fmap (\(((ret, TypeA a), TypeB b), TypeC c) -> ((a, b, c), ret)) $
  Prim.runBagger $
  Prim.stackItem (TypeC c) $
  Prim.stackItem (TypeB b) $
  Prim.stackItem (TypeA a) $
  Prim.makeBag m

runPureBag ::
    (Int, Int, Int) ->
    Pure.StateBagT '[TypeA, TypeB, TypeC] Identity a ->
    ((Int, Int, Int), a)
runPureBag (a, b, c) m =
  runIdentity $
  fmap (\(((ret, TypeA a), TypeB b), TypeC c) -> ((a, b, c), ret)) $
  Pure.runBagger $
  Pure.stackItem (TypeC c) $
  Pure.stackItem (TypeB b) $
  Pure.stackItem (TypeA a) $
  Pure.makeBag m

getABC :: (StateBagMonad m, Bag m ~ '[TypeA, TypeB, TypeC]) =>
    m (Int, Int, Int)
getABC = do
    (TypeA a) <- getItem
    (TypeB b) <- getItem
    (TypeC c) <- getItem
    return (a, b, c)

putABC :: (StateBagMonad m, Bag m ~ '[TypeA, TypeB, TypeC]) =>
    (Int, Int, Int) -> m ()
putABC (a, b, c) = do
    putItem $ TypeA a
    putItem $ TypeB b
    putItem $ TypeC c

modifyABC :: (StateBagMonad m, Bag m ~ '[TypeA, TypeB, TypeC]) =>
    (Int -> Int, Int -> Int, Int -> Int) -> m ()
modifyABC (a, b, c) = do
    modifyItemM (\(TypeA x) -> return $ TypeA $ a x)
    modifyItemM (\(TypeB x) -> return $ TypeB $ b x)
    modifyItemM (\(TypeC x) -> return $ TypeC $ c x)