timers-tick 0.2.0.0 → 0.3.0.0
raw patch · 4 files changed
+123/−70 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Control.Timer.Tick: data TimedRes a
- Control.Timer.Tick: instance GHC.Base.Functor Control.Timer.Tick.TimedRes
- Control.Timer.Tick: instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: instance GHC.Generics.Generic (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: instance GHC.Show.Show a => GHC.Show.Show (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: type Timer = TimedRes ()
+ Control.Timer.Tick: Elapse :: ExpBehaviour
+ Control.Timer.Tick: Reach :: ExpBehaviour
+ Control.Timer.Tick: creaBoolTimer :: Integer -> Timed Bool
+ Control.Timer.Tick: data ExpBehaviour
+ Control.Timer.Tick: data Timed a
+ Control.Timer.Tick: instance GHC.Base.Functor Control.Timer.Tick.Timed
+ Control.Timer.Tick: instance GHC.Classes.Eq Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Timer.Tick.Timed a)
+ Control.Timer.Tick: instance GHC.Generics.Generic (Control.Timer.Tick.Timed a)
+ Control.Timer.Tick: instance GHC.Generics.Generic Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Show.Show Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Show.Show a => GHC.Show.Show (Control.Timer.Tick.Timed a)
- Control.Timer.Tick: Times :: Integer -> Loop
+ Control.Timer.Tick: Times :: Integer -> ExpBehaviour -> Loop
- Control.Timer.Tick: creaTimedRes :: Loop -> [(Integer, a)] -> TimedRes a
+ Control.Timer.Tick: creaTimedRes :: Loop -> [(Integer, a)] -> Timed a
- Control.Timer.Tick: creaTimer :: Integer -> Timer
+ Control.Timer.Tick: creaTimer :: a -> a -> Integer -> Timed a
- Control.Timer.Tick: fetchFrame :: TimedRes a -> a
+ Control.Timer.Tick: fetchFrame :: Timed a -> a
- Control.Timer.Tick: getFrames :: TimedRes a -> [(Integer, a)]
+ Control.Timer.Tick: getFrames :: Timed a -> [(Integer, a)]
- Control.Timer.Tick: isExpired :: TimedRes a -> Bool
+ Control.Timer.Tick: isExpired :: Timed a -> Bool
- Control.Timer.Tick: isLive :: TimedRes a -> Bool
+ Control.Timer.Tick: isLive :: Timed a -> Bool
- Control.Timer.Tick: reset :: TimedRes a -> TimedRes a
+ Control.Timer.Tick: reset :: Timed a -> Timed a
- Control.Timer.Tick: tick :: TimedRes a -> TimedRes a
+ Control.Timer.Tick: tick :: Timed a -> Timed a
- Control.Timer.Tick: ticks :: Integer -> TimedRes a -> TimedRes a
+ Control.Timer.Tick: ticks :: Integer -> Timed a -> Timed a
Files
- changes.txt +7/−1
- src/Control/Timer/Tick.hs +99/−62
- test/Control/Timer/TickSpec.hs +16/−6
- timers-tick.cabal +1/−1
changes.txt view
@@ -1,10 +1,16 @@+0.3.0.0+-------++- Reworked the API, added Reach ending, fixed bugs.+- Released on Thu 15 Mar 2018 08:49:30 CET.+ 0.2.0.0 ------- - Renamed `fetch` to `fetchFrame`. - Added `getFrames`. - Added Functor instance-- Released Thu 15 Mar 2018 03:33:20 CET+- Released on Thu 15 Mar 2018 03:33:20 CET 0.1.2.0 -------
src/Control/Timer/Tick.hs view
@@ -17,11 +17,12 @@ module Control.Timer.Tick ( -- * Simple timers creaTimer,- Timer,+ creaBoolTimer, -- * Timed Resources- TimedRes,+ Timed, creaTimedRes, Loop(..),+ ExpBehaviour(..), -- * Use tick, ticks,@@ -48,77 +49,100 @@ -- Example: -- -- @--- run = creaTimedRes (Times 1) [(2, "a "), (1, "b "), (2, "c ")]--- main = count run--- where--- count t | isExpired t = putStrLn "\nOver!"--- | otherwise = do putStr (fetch t)--- count (tick t)--- -- λ> main--- -- a a b c c--- -- Over!+-- timer = creaTimedRes (Times 1 Elapse) [(2, "a "), (1, "b "), (2, "c ")]+-- test t | isExpired t = putStrLn "Fine."+-- | otherwise = do putStr (fetchFrame t)+-- test (tick t)+--+-- -- λ> test timer+-- -- a a b c c Fine. -- @-data TimedRes a = TimedRes { -- init- tSteps :: [TimerStep a],- tLoop :: Loop,- tOrigLoop :: Loop,+data Timed a = TimedRes { -- init+ tSteps :: [TimerStep a],+ tLoop :: Loop,+ tOrigLoop :: Loop, - -- convenience- tMaxTicks :: Integer,+ -- convenience+ tMaxTicks :: Maybe Integer, - -- curr- tCurrTick :: Integer,- tExpired :: Bool- }+ -- curr+ tCurrTick :: Integer,+ tExpired :: Bool+ } deriving (Show, Eq, Generic) type TimerStep a = (Integer, a) -- | Number of times to repeat the animation.-data Loop = Times Integer -- currentLoop and maxLoop- | AlwaysLoop+data Loop = -- | Loops forever, never expires.+ AlwaysLoop+ -- | Repeats the cycle for a fixed number of times.+ | Times Integer ExpBehaviour deriving (Show, Eq, Generic) +-- | Expire behaviour.+data ExpBehaviour =+ -- | Expires upon __reaching__ last frame.+ Reach+ -- | Expires when last frame is __over__.+ | Elapse+ deriving (Show, Eq, Generic)+ -- todo Monoid (or semigroup) <> for timers [2.0] --- | For easy mapping on frames.-instance Functor TimedRes where+-- | Mapping on frames.+instance Functor Timed where fmap f t = t { tSteps = fmap (\(i, a) -> (i, f a)) (tSteps t) } +-- todo [release] modifice esempi per renderli one liner o ghci+-- firnedly+ ------------ -- CREATE -- ------------ -type Timer = TimedRes ()+-- todo 0-length frames [release]+-- todo inverted timer (expires on Nothing) [release] --- | Creates a 'Timer' expiring in @x@ ticks.+-- | A simple off/on timer expiring in fixed number of ticks. -- -- Example: -- -- @--- main = count (creaTimer 4)--- where--- count t | isExpired t = putStrLn "Over!"--- | otherwise = do putStrLn "Ticking."--- count (tick t)+-- timer = creaTimer Nothing (Just "Over!") 4+-- test t | isExpired t = print (fetchFrame t)+-- | otherwise = do print (fetchFrame t)+-- test (tick t) ----- -- λ> main--- -- Ticking.--- -- Ticking.--- -- Ticking.--- -- Ticking.--- -- Over!+-- -- λ> test timer+-- -- Nothing+-- -- Nothing+-- -- Nothing+-- -- Nothing+-- -- Just \"Over\"! -- @-creaTimer :: Integer -> Timer-creaTimer c = creaTimedRes (Times 1) [(c, ())]+creaTimer :: a -> a -> Integer -> Timed a+creaTimer off on i = creaTimedRes (Times 1 Reach) [(i, off), (1, on)] --- | Creates a time-based resource, like an animation.-creaTimedRes :: Loop -> [(Integer, a)] -> TimedRes a+-- | Shorthand for: @'creaTimer' False True i@.+creaBoolTimer :: Integer -> Timed Bool+creaBoolTimer i = creaTimer False True i++-- | Most generic way to create a time-based resource (like an animation).+-- 'Loop' controls the expiring behaviour, @[(Integer, a)]@ is a list of+-- frames and their duration.+creaTimedRes :: Loop -> [(Integer, a)] -> Timed a creaTimedRes _ [] = error "Cannot create an empty TimedRes" creaTimedRes l ss = TimedRes ss l l- (sum . map fst $ ss)- 0 False+ maxTricks+ 0 False+ where+ maxTricks :: Maybe Integer+ maxTricks = case l of+ AlwaysLoop -> Nothing+ Times _ Elapse -> Just (sum . map fst $ ss)+ Times _ Reach -> Just (sum . map fst $ init ss) -------------@@ -126,52 +150,65 @@ ------------- -- | Ticks the timer (one step).-tick :: TimedRes a -> TimedRes a+tick :: Timed a -> Timed a tick t | isExpired t = t | otherwise = let t' = t { tCurrTick = succ (tCurrTick t) } in - if tCurrTick t' == tMaxTicks t'- then maxed t'+ if Just (tCurrTick t') == tMaxTicks t'+ then expire t' else t'++expire :: Timed a -> Timed a+expire tm = -- need this as last tick on Elapse is OOB+ if isElB (tLoop tm)+ then expx { tCurrTick = tCurrTick tm - 1 }+ else expx where- maxed :: TimedRes a -> TimedRes a- maxed tm = case tLoop tm of- Times 1 -> tm { tLoop = Times 0,- tExpired = True }- AlwaysLoop -> tm { tCurrTick = 0 }- Times n -> tm { tLoop = Times (n-1),- tCurrTick = 0 }+ expx = case tLoop tm of+ -- infinite loop+ AlwaysLoop -> tm { tCurrTick = 0 }+ -- last loop of a timed resource+ Times 1 eb -> tm { tLoop = Times 0 eb,+ tExpired = True }+ -- not-last loop of a timed resource+ Times n eb -> tm { tLoop = Times (n-1) eb,+ tCurrTick = 0 } + isElB (Times _ Elapse) = True+ isElB _ = False+ -- | Ticks the timer (multiple steps).-ticks :: Integer -> TimedRes a -> TimedRes a+ticks :: Integer -> Timed a -> Timed a ticks 1 t = tick t ticks n t | n < 1 = error "negative number passed to `ticks`" | otherwise = ticks (n-1) (tick t) --- | Equal to @not isExpired@.-isLive :: TimedRes a -> Bool+-- | Antonym of 'isExpired'.+--+-- > isLive = not isExpired+isLive :: Timed a -> Bool isLive t = not $ tExpired t -- | Checks wheter the timer is expired (an expired timer will not -- respond to 'tick').-isExpired :: TimedRes a -> Bool+isExpired :: Timed a -> Bool isExpired t = tExpired t -- | Fetches the current resource of the timer.-fetchFrame :: TimedRes a -> a+fetchFrame :: Timed a -> a fetchFrame t = bl !! (fromIntegral $ tCurrTick t) where bl = concatMap (\(c, a) -> replicate (fromIntegral c) a) $ tSteps t --- | Return a list of all frames plus the display timelenght.-getFrames :: TimedRes a -> [(Integer, a)]+-- | Return a list of all frames plus their duration.+getFrames :: Timed a -> [(Integer, a)] getFrames t = tSteps t -- todo having another input apart from []? maybe a function? -- | Resets the timer to its original state.-reset :: TimedRes a -> TimedRes a+reset :: Timed a -> Timed a reset t = t { tCurrTick = 0, tExpired = False, tLoop = tOrigLoop t }
test/Control/Timer/TickSpec.hs view
@@ -12,6 +12,11 @@ spec :: Spec spec = do + describe "creaTimer" $ do+ it "should get off with the right resource" $+ let st = creaTimer 'a' 'b' 1 in+ fetchFrame (tick st) `shouldBe` 'b'+ describe "creaTimedRes" $ do it "does not allow creation of empty timed resources" $ E.evaluate (creaTimedRes AlwaysLoop [])@@ -19,11 +24,11 @@ describe "tick" $ do it "ticks a simple timer" $- let st = creaTimer 1 in+ let st = creaBoolTimer 1 in isExpired (tick st) `shouldBe` True describe "ticks" $ do- let t = creaTimer 10+ let t = creaBoolTimer 10 it "fails on negative integer" $ E.evaluate (ticks (-3) t) `shouldThrow` anyException it "performs a number of ticks" $@@ -34,7 +39,7 @@ isExpired (ticks 80 t) `shouldBe` True describe "loops" $ do- let t = creaTimedRes (Times 2) [(1,())]+ let t = creaTimedRes (Times 2 Elapse) [(1,())] let ta = creaTimedRes AlwaysLoop [(1,())] it "loops appropriately" $ isExpired (ticks 2 t) `shouldBe` True@@ -44,19 +49,24 @@ isExpired (ticks 1 t) `shouldBe` False describe "isExpired" $ do- let st = creaTimer 10+ let st = creaBoolTimer 10 it "checks if a timer is expired" $ isExpired (ticks 100 st) `shouldBe` True it "complements isLive" $ isExpired (ticks 100 st) `shouldBe` not (isLive (ticks 100 st))+ it "expires on last frame with Reach" $+ let st1 = creaTimedRes (Times 1 Reach) [(1, 'a'), (1, 'b')] in+ isExpired (ticks 1 st1) `shouldBe` True describe "fetchFrame" $ do- let ta = creaTimedRes (Times 1) [(1,'a'), (2, 'b'), (1, 'c')]+ let ta = creaTimedRes (Times 1 Elapse) [(1,'a'), (2, 'b'), (1, 'c')] it "gets the underlying resoure" $ fetchFrame (ticks 3 ta) `shouldBe` 'c'+ it "does not choke on last tick" $+ fetchFrame (ticks 10 ta) `shouldBe` 'c' describe "reset" $ do- let t = creaTimer 10+ let t = creaBoolTimer 10 it "resets the timer" $ reset (ticks 30 t) `shouldBe` t
timers-tick.cabal view
@@ -1,5 +1,5 @@ name: timers-tick-version: 0.2.0.0+version: 0.3.0.0 synopsis: tick based timers description: Tick-based timers and utilities, for games and discrete-time programs.