varying 0.7.1.1 → 0.8.0.0
raw patch · 7 files changed
+315/−220 lines, 7 filesdep +doctestdep ~basedep ~transformersPVP ok
version bump matches the API change (PVP)
Dependencies added: doctest
Dependency ranges changed: base, transformers
API changes (from Hackage documentation)
- Control.Varying.Tween: type TweenT f t m = SplineT f t (StateT f m)
+ Control.Varying.Tween: data TweenT f t m a
+ Control.Varying.Tween: instance Control.Monad.Trans.Class.MonadTrans (Control.Varying.Tween.TweenT f t)
+ Control.Varying.Tween: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Varying.Tween.TweenT f t m)
+ Control.Varying.Tween: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Varying.Tween.TweenT f t m)
+ Control.Varying.Tween: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Varying.Tween.TweenT f t m)
+ Control.Varying.Tween: instance GHC.Generics.Generic (Control.Varying.Tween.TweenT f t m a)
- Control.Varying.Spline: capture :: Monad m => SplineT a b m c -> SplineT a b m (Event b, c)
+ Control.Varying.Spline: capture :: Monad m => SplineT a b m c -> SplineT a b m (Maybe b, c)
- Control.Varying.Tween: runTweenT :: TweenT f t m x -> f -> f -> m (Either x (t, TweenT f t m x), f)
+ Control.Varying.Tween: runTweenT :: Functor m => TweenT f t m a -> f -> f -> m (Either a (t, TweenT f t m a), f)
- Control.Varying.Tween: tweenStream :: (Monad m, Num f) => TweenT f t m x -> t -> VarT m f t
+ Control.Varying.Tween: tweenStream :: forall m f t x. (Functor m, Monad m, Num f) => TweenT f t m x -> t -> VarT m f t
- Control.Varying.Tween: type Tween f t = TweenT f t Identity
+ Control.Varying.Tween: type Tween f t a = TweenT f t Identity a
Files
- changelog.md +7/−3
- src/Control/Varying.hs +3/−0
- src/Control/Varying/Core.hs +2/−0
- src/Control/Varying/Spline.hs +30/−15
- src/Control/Varying/Tween.hs +171/−64
- test/DocTests.hs +7/−0
- varying.cabal +95/−138
changelog.md view
@@ -27,6 +27,10 @@ 0.7.0.0 - added proofs, reduced API size by removing trivial or weird (special) combinators, changed some names, Event is a synonym of Maybe, removed- Time (moved functions to Event), renamed Event.mergeE to Event.bothE,- added Spline.untilProc and Spline.whileProc, documentation - working - towards 1.0+ Time (moved functions to Event), renamed Event.mergeE to Event.bothE,+ added Spline.untilProc and Spline.whileProc, documentation - working+ towards 1.0++0.7.1.2 - Fixed broken ArrowLoop instance, updated documentation.++0.8.0.0 - TweenT is a newtype.
src/Control/Varying.hs view
@@ -27,3 +27,6 @@ import Control.Varying.Event as V import Control.Varying.Spline as V import Control.Varying.Tween as V++-- TODO: CICD and auto-push master to hackage.+-- This would be very nice.
src/Control/Varying/Core.hs view
@@ -92,6 +92,7 @@ where g (b, vb) = (f b, f <$> vb) -- | A var is a category.+-- -- @ -- id = var id -- f . g = g >>> f@@ -180,6 +181,7 @@ -- | Inputs can depend on outputs as long as no time-travel is required. -- -- This isn't the best example but it does make a good test case:+-- -- >>> :{ -- let -- testVar :: VarT IO Double (Maybe Double)
src/Control/Varying/Spline.hs view
@@ -15,7 +15,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE TupleSections #-} module Control.Varying.Spline ( -- * Spline Spline@@ -51,10 +50,7 @@ import Control.Varying.Core import Control.Varying.Event import Data.Functor.Identity-import Data.Monoid --- $setup--- >>> import Control.Varying.Time -- | 'SplineT' shares all the types of 'VarT' and adds a result value. Its -- monad, input and output types (@m@, @a@ and @b@, respectively) represent the@@ -101,6 +97,7 @@ Left c -> runSplineT (f c) a Right (b, SplineT s1) -> return $ Right (b, SplineT $ g s1) + -- | A spline responds to 'pure' by returning a spline that never produces an -- output value and immediately returns the argument. It responds to '<*>' by -- applying the left arguments result value (the function) to the right@@ -117,9 +114,9 @@ pure = return sf <*> sx = do f <- sf- x <- sx- return $ f x+ f <$> sx + -- | A spline is a transformer by running the effect and immediately concluding, -- using the effect's result as the result value. --@@ -315,21 +312,39 @@ -- spline's result. This is helpful when you want to sample the last -- output value in order to determine the next spline to sequence. --+-- The tupled value is returned in as a 'Maybe b' since it is not+-- guaranteed that an output value is produced before a Spline concludes.+-- -- >>> :{--- let s = do (Just x, "boom") <- capture $ do step 0--- step 1--- step 2--- return "boom"--- -- x is 2--- step $ x + 1--- in testVarOver (outputStream s 666) [(),(),(),()]+-- let+-- s :: MonadIO m => SplineT () Int m String+-- s = do+-- (mayX, boomStr) <-+-- capture+-- $ do+-- step 0+-- step 1+-- step 2+-- return "boom"+-- -- x is 2, but 'capture' can't be sure of that+-- maybe+-- (return "Failure")+-- ( (>> return boomStr)+-- . step+-- . (+1)+-- )+-- mayX+-- in+-- testVarOver (outputStream s 666) [(),(),(),()] -- >>> :} -- 0 -- 1 -- 2 -- 3-capture :: Monad m- => SplineT a b m c -> SplineT a b m (Event b, c)+capture+ :: Monad m+ => SplineT a b m c+ -> SplineT a b m (Maybe b, c) capture = SplineT . f Nothing where f mb s = runSplineT s >=> return . \case Left c -> Left (mb, c)
src/Control/Varying/Tween.hs view
@@ -13,17 +13,15 @@ -- time you use. At some point it would be great to be able to tween -- arbitrary types, and possibly tween one type into another (pipe -- dreams).--{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-} module Control.Varying.Tween ( -- * Tweening types Easing , TweenT , Tween- -- * Running tweens- , runTweenT- , scanTween- , tweenStream -- * Creating tweens -- $creation , tween@@ -31,6 +29,8 @@ , constant , withTween , withTween_+ -- * Combining tweens+ -- $combining -- * Interpolation functions -- $lerping , linear@@ -47,26 +47,50 @@ , easeOutCubic , easeInQuad , easeOutQuad- -- * Writing your own tweens- -- $writing+ -- * Running tweens+ , tweenStream+ , runTweenT+ , scanTween ) where import Control.Monad (void) import Control.Monad.Trans.State (StateT, evalStateT, get, put, runStateT)+import Control.Monad.Trans.Class (MonadTrans (..)) import Control.Varying.Core (VarT (..), done)+import Control.Varying.Event (after) import Control.Varying.Spline (SplineT (..), mapOutput, scanSpline, untilEvent_)-import Control.Varying.Event (after)+import Data.Bifunctor (first, second) import Data.Functor.Identity (Identity)+import GHC.Generics (Generic) ++-- $setup+-- >>> import Control.Varying.Core++ --------------------------------------------------------------------------------+-- | An easing function. The parameters are often named `c`, `t` and `b`,+-- where `c` is the total change in value over the complete duration+-- (endValue - startValue), `t` is the current percentage (0 to 1) of the+-- duration that has elapsed and `b` is the start value.+--+-- To make things simple only numerical values can be tweened and the type+-- of time deltas must match the tween's value type. This may change in the+-- future :)+type Easing t f = t -> f -> t -> t+++-------------------------------------------------------------------------------- -- $lerping -- These pure functions take a `c` (total change in value, ie end - start), -- `t` (percent of duration completion) and `b` (start value) and result in -- an interpolation of a value. To see what these look like please check -- out http://www.gizma.com/easing/. --------------------------------------------------------------------------------++ -- | Ease in quadratic. easeInQuad :: (Fractional t, Real f) => Easing t f easeInQuad c t b = c * realToFrac (t*t) + b@@ -131,30 +155,121 @@ linear :: (Floating t, Real f) => Easing t f linear c t b = c * realToFrac t + b --- TODO: Don't use StateT for leftover time in Tweens.--- This creates a funky state where running two tween splines together--- causes leftover time interplay. Think about continuations or something.+-- | A 'TweenT' is a 'SplineT' that holds a duration in local state. This allows+-- 'TweenT's to be sequenced monadically.+--+-- * 'f' is the input time delta type (the input type)+-- * 't' is the start and end value type (the output type)+-- * 'a' is the result value type+--+-- You can sequence 'TweenT's with monadic notation to produce more complex ones.+-- This is especially useful for animation:+--+-- >>> :{+-- let+-- tweenInOutExpo+-- :: ( Monad m, Floating t, Real t, Real f, Fractional f )+-- => t+-- -> t+-- -> f+-- -> TweenT f t m t+-- tweenInOutExpo start end dur = do+-- x <- tween easeInExpo start (end/2) (dur/2)+-- tween easeOutExpo x end $ dur/2+-- >>> :}+newtype TweenT f t m a+ = TweenT { unTweenT :: SplineT f t (StateT f m) a }+ deriving (Generic, Functor, Applicative, Monad) -type TweenT f t m = SplineT f t (StateT f m)-type Tween f t = TweenT f t Identity -runTweenT :: TweenT f t m x -> f -> f -> m (Either x (t, TweenT f t m x), f)-runTweenT s dt = runStateT (runSplineT s dt)+instance MonadTrans (TweenT f t) where+ lift = TweenT . lift . lift -scanTween :: (Monad m, Num f)- => TweenT f t m a -> t -> [f] -> m [t]-scanTween s t dts = evalStateT (scanSpline s t dts) 0 +type Tween f t a = TweenT f t Identity a+++runTweenT+ :: Functor m+ => TweenT f t m a+ -> f+ -- ^ The input time delta this frame+ -> f+ -- ^ The leftover time delta from last frame+ -> m (Either a (t, TweenT f t m a), f)+ -- ^ Returns+ -- @+ -- a tuple of+ -- either+ -- the result+ -- or a tuple of+ -- this step's output value+ -- and the tween for the next step+ -- and the leftover time delta for the next step+ -- @+runTweenT (TweenT s) dt leftover =+ first (second $ second TweenT)+ <$> runStateT+ (runSplineT s dt)+ leftover+++scanTween+ :: (Monad m, Num f)+ => TweenT f t m a+ -> t+ -> [f]+ -> m [t]+scanTween (TweenT s) t dts =+ evalStateT+ (scanSpline s t dts)+ 0++ -- | Converts a tween into a continuous value stream. This is the tween version--- of `outputStream`.-tweenStream :: (Monad m, Num f)- => TweenT f t m x -> t -> VarT m f t-tweenStream s0 t0 = VarT $ f s0 t0 0- where f s t l i = do (e, l1) <- runTweenT s i l- case e of- Left _ -> return (t, done t)- Right (b, s1) -> return (b, VarT $ f s1 b l1)+-- of 'Control.Varying.Spline.outputStream'. This is the preferred way to run+-- your tweens.+--+-- >>> :{+-- let+-- x :: TweenT Float Float IO Float+-- x = tween linear 0 1 1+-- y :: TweenT Float Float IO Float+-- y = tween linear 0 1 2+-- v :: VarT IO Float (Float, Float)+-- v = (,)+-- <$> tweenStream x 0+-- <*> tweenStream y 0+-- in+-- testVarOver v [0.5, 0.5, 0.5, 0.5]+-- >>> :}+-- (0.5,0.25)+-- (1.0,0.5)+-- (1.0,0.75)+-- (1.0,1.0)+tweenStream+ :: forall m f t x+ . (Functor m, Monad m, Num f)+ => TweenT f t m x+ -- ^ The tween to convert into a stream+ -> t+ -- ^ An initial output value+ -> VarT m f t+tweenStream s0 t0 = VarT $ go s0 t0 0+ where+ go ::+ TweenT f t m x -- The Tween+ -> t -- the last output value+ -> f -- the leftover time delta from last fram+ -> f -- the input time delta+ -> m (t, VarT m f t)+ go s t l i = do+ (e, l1) <- runTweenT s i l+ case e of+ Left _ -> return (t, done t)+ Right (b, s1) -> return (b, VarT $ go s1 b l1) + -------------------------------------------------------------------------------- -- $creation -- The most direct route toward tweening values is to use 'tween'@@ -174,21 +289,26 @@ -- `tween` concludes returning the latest output value. tween :: (Monad m, Real t, Real f, Fractional f) => Easing t f -> t -> t -> f -> TweenT f t m t-tween f start end dur = SplineT g- where c = end - start- b = start- g dt = do- leftover <- get- let t = dt + leftover+tween f start end dur =+ TweenT+ $ SplineT g+ where+ c = end - start+ b = start+ g dt = do+ leftover <- get+ let+ t = dt + leftover+ if t == dur+ then+ put 0 >> return (Right (end, return end))+ else+ if t > dur+ then+ put (t - dur - dt) >> return (Left end)+ else+ put t >> return (Right (f c (t/dur) b, SplineT g)) - if t == dur- then do put 0- return $ Right (end, return end)- else if t > dur- then do put $ t - dur - dt- return $ Left end- else do put t- return $ Right (f c (t/dur) b, SplineT g) -- | A version of 'tween' that discards the result. It is simply --@@ -202,14 +322,19 @@ -- | A version of 'tween' that maps its output using the given constant -- function.+-- -- @ -- withTween ease from to dur f = mapOutput (pure f) $ tween ease from to dur -- @ withTween :: (Monad m, Real t, Real a, Fractional a) => Easing t a -> t -> t -> a -> (t -> x) -> TweenT a x m t-withTween ease from to dur f = mapOutput (pure f) $ tween ease from to dur+withTween ease from to dur f =+ TweenT+ $ mapOutput (pure f)+ $ unTweenT+ $ tween ease from to dur --- | A version of 'withTween' that discards its output.+-- | A version of 'withTween' that discards its result. withTween_ :: (Monad m, Real t, Real a, Fractional a) => Easing t a -> t -> t -> a -> (t -> x) -> TweenT a x m () withTween_ ease from to dur f = Control.Monad.void (withTween ease from to dur f)@@ -217,24 +342,6 @@ -- | Creates a tween that performs no interpolation over the duration. constant :: (Monad m, Num t, Ord t) => a -> t -> TweenT t a m a-constant value duration = pure value `untilEvent_` after duration------------------------------------------------------------------------------------ $writing--- To create your own tweens just write a function that takes a start--- value, end value and a duration and return an event stream.------ @--- tweenInOutExpo start end dur = do--- (dt, x) <- tween easeInExpo start end (dur/2)--- tween easeOutExpo x end $ dt + dur/2--- @------------------------------------------------------------------------------------ | An easing function. The parameters are often named `c`, `t` and `b`,--- where `c` is the total change in value over the complete duration--- (endValue - startValue), `t` is the current percentage (0 to 1) of the--- duration that has elapsed and `b` is the start value.------ To make things simple only numerical values can be tweened and the type--- of time deltas must match the tween's value type. This may change in the--- future :)-type Easing t f = t -> f -> t -> t+constant value duration =+ TweenT+ $ pure value `untilEvent_` after duration
+ test/DocTests.hs view
@@ -0,0 +1,7 @@+module Main where++import Test.DocTest++main :: IO ()+main =+ doctest ["src", "app"]
varying.cabal view
@@ -1,150 +1,107 @@--- Initial varying.cabal generated by cabal init. For further--- documentation, see http://haskell.org/cabal/users-guide/---- The name of the package.-name: varying---- The package version. See the Haskell package versioning policy (PVP)--- for standards guiding when and how versions should be incremented.--- http://www.haskell.org/haskellwiki/Package_versioning_policy--- PVP summary: +-+------- breaking API changes--- | | +----- non-breaking API additions--- | | | +--- code changes with no API change-version: 0.7.1.1---- A short (one-line) description of the package.-synopsis: FRP through value streams and monadic splines.---- A longer description of the package.-description: Varying is a FRP library aimed at providing a- simple way to describe values that change over a domain.- It allows monadic, applicative and arrow notation and has- convenience functions for tweening.---- URL for the project homepage or repository.-homepage: https://github.com/schell/varying---- The license under which the package is released.-license: MIT---- The file containing the license text.-license-file: LICENSE---- The package author(s).-author: Schell Scivally---- An email address to which users can send suggestions, bug reports, and--- patches.-maintainer: schell@takt.com---- A copyright notice.--- copyright:--category: Control, FRP--build-type: Simple---- Extra files to be distributed with the package, such as examples or a--- README.--- extra-source-files:+cabal-version: 1.12 --- Constraint on the version of Cabal needed to build this package.-cabal-version: >=1.10+-- This file has been generated from package.yaml by hpack version 0.31.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 23f79975d60664192d4aec0699c6a80542dc85db8ebfb018fd84bea51c196f7b -extra-source-files: README.md, changelog.md+name: varying+version: 0.8.0.0+synopsis: FRP through value streams and monadic splines.+description: Varying is a FRP library aimed at providing a simple way to describe values that change over a domain. It allows monadic, applicative and arrow notation and has convenience functions for tweening. Great for animation.+category: Control, FRP+homepage: https://github.com/schell/varying+bug-reports: https://github.com/schell/varying/issues+author: Schell Scivally+maintainer: schell@takt.com+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ changelog.md source-repository head- type: git- location: https://github.com/schell/varying.git+ type: git+ location: https://github.com/schell/varying library- ghc-options: -Wall- if impl(ghc >= 8)- ghc-options: -Wno-type-defaults-- -- Modules exported by the library.- exposed-modules: Control.Varying,- Control.Varying.Core,- Control.Varying.Event,- Control.Varying.Tween,- Control.Varying.Spline-- -- Modules included in this library but not exported.- -- other-modules:-- -- LANGUAGE extensions used by modules in this package.- -- other-extensions:-- -- Other library packages from which modules are imported.- build-depends: base >=4.8 && <5.0- , transformers >=0.3- , contravariant >= 1.4-- -- Directories containing source files.- hs-source-dirs: src-- -- Base language which the package is written in.- default-language: Haskell2010+ exposed-modules:+ Control.Varying+ Control.Varying.Core+ Control.Varying.Event+ Control.Varying.Spline+ Control.Varying.Tween+ other-modules:+ Paths_varying+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ base >=4.8 && <5.0+ , contravariant >=1.4+ , transformers >=0.3+ default-language: Haskell2010 executable varying-example- ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- if impl(ghc >= 8)- ghc-options: -Wno-type-defaults-- -- Other library packages from which modules are imported.- build-depends: base >=4.8 && <5.0- , transformers >=0.3- , time >=1.4- , varying--- -- Directories containing source files.- hs-source-dirs: app-- main-is: Main.hs-- -- Base language which the package is written in.- default-language: Haskell2010--test-suite varying-test- type: exitcode-stdio-1.0- ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- if impl(ghc >= 8)- ghc-options: -Wno-type-defaults-- -- Other library packages from which modules are imported.- build-depends: base >=4.8 && <5.0- , time >=1.4- , transformers- , varying- , hspec- , QuickCheck-- -- Directories containing source files.- hs-source-dirs: test+ main-is: Main.hs+ other-modules:+ Paths_varying+ hs-source-dirs:+ app+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.8 && <5.0+ , contravariant >=1.4+ , time >=1.4+ , transformers >=0.3+ , varying+ default-language: Haskell2010 - main-is: Main.hs+test-suite doctests+ type: exitcode-stdio-1.0+ main-is: DocTests.hs+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.8 && <5.0+ , contravariant >=1.4+ , doctest+ , transformers >=0.3+ , varying+ default-language: Haskell2010 - -- Base language which the package is written in.- default-language: Haskell2010+test-suite other+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ QuickCheck+ , base >=4.8 && <5.0+ , contravariant >=1.4+ , hspec+ , time >=1.4+ , transformers >=0.3+ , varying+ default-language: Haskell2010 benchmark varying-bench- type: exitcode-stdio-1.0- ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- if impl(ghc >= 8)- ghc-options: -Wno-type-defaults-- -- Other library packages from which modules are imported.- build-depends: base >=4.8- , time >=1.4- , transformers- , varying- , criterion-- -- Directories containing source files.- hs-source-dirs: bench-- main-is: Main.hs-- -- Base language which the package is written in.- default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ Paths_varying+ hs-source-dirs:+ bench+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.8 && <5.0+ , contravariant >=1.4+ , criterion+ , time >=1.4+ , transformers+ , varying+ default-language: Haskell2010