diff --git a/dunai-frp-bearriver/CHANGELOG b/dunai-frp-bearriver/CHANGELOG index 251a1760..6574bb33 100644 --- a/dunai-frp-bearriver/CHANGELOG +++ b/dunai-frp-bearriver/CHANGELOG @@ -1,3 +1,7 @@ +2023-08-21 Ivan Perez + * Version bump (0.14.4) (#377). + * Offer all definitions from FRP.Yampa.Basic (#376). + 2023-06-21 Ivan Perez * Version bump (0.14.3) (#372). * Offer all definitions from FRP.Yampa.Arrow (#360). diff --git a/dunai-frp-bearriver/bearriver.cabal b/dunai-frp-bearriver/bearriver.cabal index a97182e6..5d8a6781 100644 --- a/dunai-frp-bearriver/bearriver.cabal +++ b/dunai-frp-bearriver/bearriver.cabal @@ -30,7 +30,7 @@ cabal-version: >= 1.10 build-type: Simple name: bearriver -version: 0.14.3 +version: 0.14.4 author: Ivan Perez, Manuel Bärenz maintainer: ivan.perez@keera.co.uk homepage: https://github.com/ivanperez-keera/dunai @@ -77,8 +77,12 @@ library exposed-modules: FRP.BearRiver FRP.BearRiver.Arrow + FRP.BearRiver.Basic FRP.Yampa + other-modules: + FRP.BearRiver.InternalCore + build-depends: base >= 4.6 && <5 , deepseq >= 1.3.0.0 && < 1.5 diff --git a/dunai-frp-bearriver/src/FRP/BearRiver.hs b/dunai-frp-bearriver/src/FRP/BearRiver.hs index 381fbf6b..7a9c472e 100644 --- a/dunai-frp-bearriver/src/FRP/BearRiver.hs +++ b/dunai-frp-bearriver/src/FRP/BearRiver.hs @@ -26,7 +26,6 @@ import Control.Applicative (Applicative (..), (<$>)) #endif import Control.Applicative (Alternative (..)) import Control.Arrow as X -import qualified Control.Category as Category import Control.DeepSeq (NFData (..)) import qualified Control.Monad.Fail as Fail import Control.Monad.Random (MonadRandom) @@ -47,36 +46,16 @@ import Data.MonadicStreamFunction as X hiding trace) import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) import FRP.BearRiver.Arrow as X +import FRP.BearRiver.Basic as X +import FRP.BearRiver.InternalCore as X -- Internal imports (dunai, instances) import Data.MonadicStreamFunction.Instances.ArrowLoop () -- not needed, just -- re-exported - -infixr 0 -->, -:>, >--, >=- + -- -- * Basic definitions --- | Time is used both for time intervals (duration), and time w.r.t. some --- agreed reference point in time. -type Time = Double - --- | DTime is the time type for lengths of sample intervals. Conceptually, --- DTime = R+ = { x in R | x > 0 }. Don't assume Time and DTime have the same --- representation. -type DTime = Double - --- | Extensible signal function (signal function with a notion of time, but --- which can be extended with actions). --- --- Signal function that transforms a signal carrying values of some type 'a' --- into a signal carrying values of some type 'b'. You can think of it as --- (Signal a -> Signal b). A signal is, conceptually, a function from 'Time' to --- value. -type SF m = MSF (ClockInfo m) - --- | Information on the progress of time. -type ClockInfo m = ReaderT DTime m - -- | A single possible event occurrence, that is, a value that may or may not -- occur. Events are used to represent values that are not produced -- continuously, such as mouse clicks (only produced when the mouse is clicked, @@ -144,22 +123,6 @@ arrEPrim = arr -- ** Basic signal functions --- | Identity: identity = arr id --- --- Using 'identity' is preferred over lifting id, since the arrow combinators --- know how to optimise certain networks based on the transformations being --- applied. -identity :: Monad m => SF m a a -identity = Category.id - --- | Identity: constant b = arr (const b) --- --- Using 'constant' is preferred over lifting const, since the arrow --- combinators know how to optimise certain networks based on the --- transformations being applied. -constant :: Monad m => b -> SF m a b -constant = arr . const - -- | Outputs the time passed since the signal function instance was started. localTime :: Monad m => SF m a Time localTime = constant 1.0 >>> integral @@ -168,40 +131,6 @@ localTime = constant 1.0 >>> integral time :: Monad m => SF m a Time time = localTime --- ** Initialization - --- | Initialization operator (cf. Lustre/Lucid Synchrone). --- --- The output at time zero is the first argument, and from that point on it --- behaves like the signal function passed as second argument. -(-->) :: Monad m => b -> SF m a b -> SF m a b -b0 --> sf = sf >>> replaceOnce b0 - --- | Output pre-insert operator. --- --- Insert a sample in the output, and from that point on, behave like the given --- sf. -(-:>) :: Monad m => b -> SF m a b -> SF m a b -b -:> sf = iPost b sf - --- | Input initialization operator. --- --- The input at time zero is the first argument, and from that point on it --- behaves like the signal function passed as second argument. -(>--) :: Monad m => a -> SF m a b -> SF m a b -a0 >-- sf = replaceOnce a0 >>> sf - --- | Transform initial input value. --- --- Applies a transformation 'f' only to the first input value at time zero. -(>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b -f >=- sf = MSF $ \a -> do - (b, sf') <- unMSF sf (f a) - return (b, sf') - --- | Override initial value of input signal. -initially :: Monad m => a -> SF m a a -initially = (--> identity) -- * Simple, stateful signal processing @@ -878,12 +807,3 @@ evalAt sf dt a = runIdentity $ runReaderT (unMSF sf a) dt -- discrete and step based. evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b) evalFuture sf = flip (evalAt sf) - --- * Auxiliary functions - --- ** Event handling - --- | Replace the value of the input signal at time zero with the given --- argument. -replaceOnce :: Monad m => a -> SF m a a -replaceOnce a = dSwitch (arr $ const (a, Event ())) (const $ arr id) diff --git a/dunai-frp-bearriver/src/FRP/BearRiver/Basic.hs b/dunai-frp-bearriver/src/FRP/BearRiver/Basic.hs new file mode 100644 index 00000000..3fcab98e --- /dev/null +++ b/dunai-frp-bearriver/src/FRP/BearRiver/Basic.hs @@ -0,0 +1,109 @@ +-- | +-- Module : FRP.BearRiver.Basic +-- Copyright : (c) Ivan Perez, 2014-2022 +-- (c) George Giorgidze, 2007-2012 +-- (c) Henrik Nilsson, 2005-2006 +-- (c) Antony Courtney and Henrik Nilsson, Yale University, 2003-2004 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Maintainer : ivan.perez@keera.co.uk +-- Stability : provisional +-- Portability : non-portable (GHC extensions) +-- +-- Defines basic signal functions, and elementary ways of altering them. +-- +-- This module defines very basic ways of creating and modifying signal +-- functions. In particular, it defines ways of creating constant output +-- producing SFs, and SFs that just pass the signal through unmodified. +-- +-- It also defines ways of altering the input and the output signal only by +-- inserting one value in the signal, or by transforming it. +module FRP.BearRiver.Basic + ( + -- * Basic signal functions + identity + , constant + + -- ** Initialization + , (-->) + , (-:>) + , (>--) + , (-=>) + , (>=-) + , initially + ) + where + +-- External imports +import qualified Control.Category as Category + +-- Internal imports (dunai) +import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) + +-- Internal imports +import FRP.BearRiver.InternalCore (SF, arr) + +infixr 0 -->, -:>, >--, -=>, >=- + +-- * Basic signal functions + +-- | Identity: identity = arr id +-- +-- Using 'identity' is preferred over lifting id, since the arrow combinators +-- know how to optimise certain networks based on the transformations being +-- applied. +identity :: Monad m => SF m a a +identity = Category.id + +-- | Identity: constant b = arr (const b) +-- +-- Using 'constant' is preferred over lifting const, since the arrow combinators +-- know how to optimise certain networks based on the transformations being +-- applied. +constant :: Monad m => b -> SF m a b +constant = arr . const + +-- * Initialization + +-- | Initialization operator (cf. Lustre/Lucid Synchrone). +-- +-- The output at time zero is the first argument, and from that point on it +-- behaves like the signal function passed as second argument. +(-->) :: Monad m => b -> SF m a b -> SF m a b +b0 --> sf = MSF $ \a -> do + (_b, sf') <- unMSF sf a + return (b0, sf') + +-- | Output pre-insert operator. +-- +-- Insert a sample in the output, and from that point on, behave like the given +-- sf. +(-:>) :: Monad m => b -> SF m a b -> SF m a b +b -:> sf = MSF $ \_a -> return (b, sf) + +-- | Input initialization operator. +-- +-- The input at time zero is the first argument, and from that point on it +-- behaves like the signal function passed as second argument. +(>--) :: Monad m => a -> SF m a b -> SF m a b +a0 >-- sf = MSF $ \_ -> unMSF sf a0 + +-- | Transform initial output value. +-- +-- Applies a transformation 'f' only to the first output value at time zero. +(-=>) :: Monad m => (b -> b) -> SF m a b -> SF m a b +f -=> sf = MSF $ \a -> do + (b, sf') <- unMSF sf a + return (f b, sf') + +-- | Transform initial input value. +-- +-- Applies a transformation 'f' only to the first input value at time zero. +(>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b +f >=- sf = MSF $ \a -> do + (b, sf') <- unMSF sf (f a) + return (b, sf') + +-- | Override initial value of input signal. +initially :: Monad m => a -> SF m a a +initially = (--> identity) diff --git a/dunai-frp-bearriver/src/FRP/BearRiver/InternalCore.hs b/dunai-frp-bearriver/src/FRP/BearRiver/InternalCore.hs new file mode 100644 index 00000000..0ede5915 --- /dev/null +++ b/dunai-frp-bearriver/src/FRP/BearRiver/InternalCore.hs @@ -0,0 +1,62 @@ +-- | +-- Module : FRP.Dunai.InternalCore +-- Copyright : (c) Ivan Perez, 2014-2022 +-- (c) George Giorgidze, 2007-2012 +-- (c) Henrik Nilsson, 2005-2006 +-- (c) Antony Courtney and Henrik Nilsson, Yale University, 2003-2004 +-- License : BSD-style (see the LICENSE file in the distribution) +-- +-- Maintainer : ivan.perez@keera.co.uk +-- Stability : provisional +-- Portability : non-portable (GHC extensions) +-- +-- Domain-specific language embedded in Haskell for programming hybrid (mixed +-- discrete-time and continuous-time) systems, extended with Monads. +-- +-- Bearriver (a tributary to the Yampa river) provides the same API as Yampa, +-- but implemented using Monadic Stream Functions underneath. SFs in BearRiver +-- take an additional monad as argument. The introduction of time is done by +-- means of an additional Reader layer. +module FRP.BearRiver.InternalCore + ( module Control.Arrow + + -- * Basic definitions + -- ** Time + , Time + , DTime + + -- ** Signal Functions + , SF + , ClockInfo + ) + where + +-- External imports +import Control.Arrow (Arrow (..), ArrowChoice (..), ArrowLoop (..), (>>>)) + +-- Internal imports (dunai) +import Control.Monad.Trans.MSF (ReaderT) +import Data.MonadicStreamFunction (MSF) + +-- * Basic type definitions with associated utilities + +-- | Time is used both for time intervals (duration), and time w.r.t. some +-- agreed reference point in time. +type Time = Double + +-- | DTime is the time type for lengths of sample intervals. Conceptually, +-- DTime = R+ = { x in R | x > 0 }. Don't assume Time and DTime have the same +-- representation. +type DTime = Double + +-- | Extensible signal function (signal function with a notion of time, but +-- which can be extended with actions). +-- +-- Signal function that transforms a signal carrying values of some type 'a' +-- into a signal carrying values of some type 'b'. You can think of it as +-- (Signal a -> Signal b). A signal is, conceptually, a function from 'Time' to +-- value. +type SF m = MSF (ClockInfo m) + +-- | Information on the progress of time. +type ClockInfo m = ReaderT DTime m diff --git a/dunai-test/CHANGELOG b/dunai-test/CHANGELOG index 56fc0f1e..3f2d09f5 100644 --- a/dunai-test/CHANGELOG +++ b/dunai-test/CHANGELOG @@ -1,3 +1,6 @@ +2023-08-21 Ivan Perez + * Version bump (0.11.2) (#377). + 2023-06-21 Ivan Perez * Version bump (0.11.1) (#372). diff --git a/dunai-test/dunai-test.cabal b/dunai-test/dunai-test.cabal index 43cec46f..8a44178b 100644 --- a/dunai-test/dunai-test.cabal +++ b/dunai-test/dunai-test.cabal @@ -30,7 +30,7 @@ cabal-version: >= 1.10 build-type: Simple name: dunai-test -version: 0.11.1 +version: 0.11.2 author: Ivan Perez maintainer: ivan.perez@keera.co.uk homepage: https://github.com/ivanperez-keera/dunai diff --git a/dunai/CHANGELOG b/dunai/CHANGELOG index d9dbe72a..f5dcea35 100644 --- a/dunai/CHANGELOG +++ b/dunai/CHANGELOG @@ -1,3 +1,7 @@ +2023-08-21 Ivan Perez + * Version bump (0.11.2) (#377). + * Introduce benchmark (#375). + 2023-06-21 Ivan Perez * Version bump (0.11.1) (#372). * Reflect new contribution process in README (#362). diff --git a/dunai/README.md b/dunai/README.md index a73d3138..e6b8dd01 100644 --- a/dunai/README.md +++ b/dunai/README.md @@ -349,6 +349,10 @@ game, but we need to run newer reliable benchmarks including every module and only definitions from `FRP.Yampa`, `FRP.BearRiver` and `Data.MonadicStreamFunction`. +Dunai includes some benchmarks as part of the main library. You are encouraged +to use them to evaluate your pull requests, and to improve the benchmarks +themselves. + # Contributions [(Back to top)](#table-of-contents) @@ -410,6 +414,10 @@ This project is split in three parts: - _Examples_: ballbounce - sample applications that work both on traditional Yampa and BearRiver. +Dunai also includes some benchmarks as part of the main library. You are +encouraged to use them to evaluate your pull requests, and to improve the +benchmarks themselves. + ## Style [(Back to top)](#table-of-contents) diff --git a/dunai/benchmarks/Bench.hs b/dunai/benchmarks/Bench.hs new file mode 100644 index 00000000..9294c7ee --- /dev/null +++ b/dunai/benchmarks/Bench.hs @@ -0,0 +1,162 @@ +-- | +-- Description : A benchmark for dunai. +-- Copyright : (c) Ivan Perez, 2023 +-- Authors : Ivan Perez +-- Maintainer : ivan.perez@keera.co.uk +-- License : BSD3 +-- +-- A benchmark for Dunai. +module Main where + +import Criterion (bench, bgroup, nf) +import Criterion.Main (defaultConfig, defaultMainWith) +import Criterion.Types (Config (csvFile, resamples, verbosity), + Verbosity (Quiet)) +import Data.Functor.Identity (runIdentity) +import Data.Time.Format (defaultTimeLocale, formatTime) +import Data.Time.LocalTime (getZonedTime) +import System.Environment (getArgs, withArgs) +import System.FilePath (()) + +import qualified Control.Category as C + +import Data.MonadicStreamFunction + +-- | Run all benchmarks. +main :: IO () +main = do + config <- customConfig + withArgs [] $ + defaultMainWith config + [ bgroup "basic" + [ bench "identity" $ nf basicIdentity 10000 + , bench "id" $ nf basicId 10000 + ] + , bgroup "compositions" + [ bench "identity" $ nf composeIdentity 10000 + , bench "idid" $ nf composeIdId 10000 + , bench "plus" $ nf composePlus 10000 + , bench "plusplus" $ nf composePlusPlus 10000 + , bench "plusmult" $ nf composePlusMult 10000 + , bench "mult" $ nf composeMult 10000 + , bench "multmult" $ nf composeMultMult 10000 + ] + , bgroup "counter" + [ bench "counter1" $ nf counter1 10000 + , bench "counter2" $ nf counter2 10000 + ] + ] + +-- * Benchmarks + +-- ** Basic + +-- | Dunai's specialized identity function. +basicIdentity :: Int -> [Int] +basicIdentity n = runIdentity $ embed sf stream + where + sf = C.id + stream = replicate n 1 + +-- | Standard function identity lifted to SFs. +basicId :: Int -> [Int] +basicId n = runIdentity $ embed sf stream + where + sf = arr id + stream = replicate n 1 + +-- ** Compositions + +-- | Composition of Dunai's specialized identity function. +composeIdentity :: Int -> [Int] +composeIdentity n = runIdentity $ embed sf stream + where + sf = C.id >>> C.id + stream = replicate n 1 + +-- | Composition of standard function identity lifted to SFs. +composeIdId :: Int -> [Int] +composeIdId n = runIdentity $ embed sf stream + where + sf = arr id >>> arr id + stream = replicate n 1 + +-- | Plus operation. +-- +-- This is not a composition; it merely exists to serve as a comparison with +-- composePlusPlus. +composePlus :: Int -> [Int] +composePlus n = runIdentity $ embed sf stream + where + sf = arr (+3) + stream = take n [1..] + +-- | Composition of addition lifted to SFs. +composePlusPlus :: Int -> [Int] +composePlusPlus n = runIdentity $ embed sf stream + where + sf = arr (+1) >>> arr (+2) + stream = take n [1..] + +-- | Composition of addition with multiplication, lifted to SFs. +composePlusMult :: Int -> [Int] +composePlusMult n = runIdentity $ embed sf stream + where + sf = arr (+100) >>> arr (*2) + stream = take n [10..] + +-- | Multiplication operation. +-- +-- This is not a composition; it merely exists to serve as a comparison with +-- composeMultMult. +composeMult :: Int -> [Int] +composeMult n = runIdentity $ embed sf stream + where + sf = arr (*20) + stream = take n [10..] + +-- | Composition of multiplication lifted to SFs. +composeMultMult :: Int -> [Int] +composeMultMult n = runIdentity $ embed sf stream + where + sf = arr (*10) >>> arr (*2) + stream = take n [10..] + +-- ** Counter + +-- | Counter without explicit seq. +counter1 :: Int -> [Int] +counter1 n = runIdentity $ embed sf stream + where + sf = feedback 0 (arr (dup . uncurry (+))) + stream = replicate n 1 + + dup x = (x, x) + +-- | Counter with explicit seq. +counter2 :: Int -> [Int] +counter2 n = runIdentity $ embed sf stream + where + sf = feedback 0 (arr ((\x -> x `seq` (x, x)). uncurry (+))) + stream = replicate n 1 + +-- * Auxiliary functions + +-- Construct a config with increased number of sampling +-- and a custom name for the report. +customConfig :: IO Config +customConfig = do + args <- getArgs + + let dir = case args of + [] -> "." + (x:xs) -> x + + -- Custom filename using the current time + timeString <- (formatTime defaultTimeLocale "%F-%H%M%S") <$> getZonedTime + let filename = concat [ timeString, "-", "bench.csv" ] + + return $ defaultConfig { csvFile = Just $ dir filename + , resamples = 100000 + , verbosity = Quiet + } diff --git a/dunai/dunai.cabal b/dunai/dunai.cabal index da1741a9..c84cdcd9 100644 --- a/dunai/dunai.cabal +++ b/dunai/dunai.cabal @@ -30,7 +30,7 @@ cabal-version: >= 1.10 build-type: Simple name: dunai -version: 0.11.1 +version: 0.11.2 author: Ivan Perez, Manuel Bärenz maintainer: ivan.perez@keera.co.uk homepage: https://github.com/ivanperez-keera/dunai @@ -217,3 +217,24 @@ test-suite regression-tests , tasty-hunit , dunai + + +benchmark dunai-bench + type: + exitcode-stdio-1.0 + + main-is: + Bench.hs + + build-depends: + base < 5 + , criterion >= 0.5.0.0 && < 1.7 + , filepath >= 1.3.0.1 && < 1.5 + , time >= 1.4 && < 1.13 + , dunai + + default-language: + Haskell2010 + + hs-source-dirs: + benchmarks