dunai 0.7.0 → 0.14.2
raw patch · 25 files changed
Files
- CHANGELOG +200/−0
- benchmarks/Bench.hs +162/−0
- dunai.cabal +194/−85
- src/Control/Arrow/Util.hs +9/−12
- src/Control/Monad/Trans/MSF.hs +7/−3
- src/Control/Monad/Trans/MSF/Except.hs +118/−97
- src/Control/Monad/Trans/MSF/List.hs +47/−19
- src/Control/Monad/Trans/MSF/Maybe.hs +46/−35
- src/Control/Monad/Trans/MSF/RWS.hs +23/−15
- src/Control/Monad/Trans/MSF/Random.hs +37/−38
- src/Control/Monad/Trans/MSF/Reader.hs +19/−16
- src/Control/Monad/Trans/MSF/State.hs +35/−23
- src/Control/Monad/Trans/MSF/Writer.hs +21/−12
- src/Data/MonadicStreamFunction.hs +36/−35
- src/Data/MonadicStreamFunction/Async.hs +55/−56
- src/Data/MonadicStreamFunction/Core.hs +74/−74
- src/Data/MonadicStreamFunction/Instances/ArrowChoice.hs +13/−6
- src/Data/MonadicStreamFunction/Instances/ArrowLoop.hs +17/−11
- src/Data/MonadicStreamFunction/Instances/ArrowPlus.hs +21/−14
- src/Data/MonadicStreamFunction/Instances/Num.hs +13/−10
- src/Data/MonadicStreamFunction/Instances/VectorSpace.hs +33/−17
- src/Data/MonadicStreamFunction/InternalCore.hs +56/−56
- src/Data/MonadicStreamFunction/Parallel.hs +14/−10
- src/Data/MonadicStreamFunction/ReactHandle.hs +18/−18
- src/Data/MonadicStreamFunction/Util.hs +50/−47
+ CHANGELOG view
@@ -0,0 +1,200 @@+2026-10-21+ * Version bump (0.14.2) (#475).+ * Fix error in execution of `cabal update` in CI (#469).+ * Constrain versions of `MonadRandom` for GHC < 8.0 (#470).+ * Bump upper bounds on `hlint`, `time`, `transformers-compat` (#471).+ * Change repository protocol in Cabal file to https (#473).++2025-02-27+ * Version bump (0.14.1) (#465).+ * Change contact address (#462).+ * Remove emails from CHANGELOG (#463).++2024-12-21+ * Version bump (0.14.0) (#460).+ * Increase upper bounds on filepath, tasty, time, hlint (#448).+ * Remove deprecated functions related to list-t (#445).++2024-10-21+ * Version bump (0.13.2) (#442).++2024-08-21+ * Version bump (0.13.1) (#430).+ * Increase upper bounds on transformers (#427).+ * Thanks to @tomsmeding.++2024-06-21+ * Version bump (0.13.0) (#420).+ * Implement List interface using list-transformer (#418).++2024-04-23+ * Version bump (0.12.3) (#411).+ * Fix typo in documentation (#410).++2024-02-21+ * Version bump (0.12.2) (#398).+ * Limit version of transformers to < 0.6 (#405).++2023-12-21+ * Version bump (0.12.1) (#395).+ * Remove postgresql repo before installation in CI script (#392).+ * Add version bounds to dependencies (#393).+ * Mark package as uncurated (#394).+ * Fix typo in CHANGELOG (#396).++2023-10-21+ * Version bump (0.12.0) (#388).+ * Increase max-backjumps in CI setup (#381).+ * Document GHC 9.8.1 support in readme (#387).++2023-08-21+ * Version bump (0.11.2) (#377).+ * Introduce benchmark (#375).++2023-06-21+ * Version bump (0.11.1) (#372).+ * Reflect new contribution process in README (#362).++2023-04-21+ * Version bump (0.11.0) (#358).+ * Update paper links to ACM, personal website (#346).++2023-02-21+ * Version bump (0.10.1) (#345).+ * Enable FlexibleContexts in VectorSpace instance module (#344).++2023-02-21+ * Version bump (0.10.0) (#342).+ * Implement dot in MSF's instance of VectorSpace (#298).+ * Adjust version bounds for simple-affine-space (#343).++2022-12-21+ * Version bump (0.9.2) (#340).+ * Bump version bounds for MonadRandom (#337).+ * Adjust to work with simple-affine-space-0.2 (#339).++2022-10-21+ * Version bump (0.9.1) (#335).+ * Add link to updated paper examples from README (#319).+ * Update Travis script (#333).+ * Style fixes (partial) (#334).++2022-08-21+ * Version bump (0.9.0) (#313).+ * Document haskell-titan support in readme (#176).+ * Re-format CHANGELOG for readability (#308).+ * Add version bounds for dependencies (#303).+ * Consistency in condition used to check GHC/base version (#295).+ * Fix typo on documentation (#304).+ * Document GHC 9.4.1 titan support in readme (#309).++2022-06-21+ * Version bump (0.8.3) (#302).+ * Simplify implementation of switch (#276).+ * Remove redundant imports (#281).+ * Remove redundant pragma (#282).+ * Use external Void type (#79).+ * Remove redundant imports (#283).+ * Disable warning pertaining to orphan instances (#284).+ * Style fixes (partial) (#285).+ * Fix missing re-export of Control.Arrow (#286).+ * Further style fixes (partial) (#287).+ * Avoid name shadowing (#291).+ * Remove outdated comments (#289).+ * Remove unused pragmas (#290).+ * Remove unnecessary comment (#293).+ * Reimplement function without TupleSections (#293).+ * Standardize haddock comment (#294).+ * Add missing documentation (#288).+ * Disable warning due to use of ListT (#297).+ * Fix oudated comment (#299).+ * Horizontal alignment style changes (#301).+ * Re-structure README (#300).++2022-04-21+ * Version bump (0.8.2) (#280).+ * Syntax rules (#271).+ * Declare CHANGELOG (#273).+ * Declare subdir of source-repository (#272).+ * Fix haddock syntax error in documentation of switch (#274).+ * Introduce new dSwitch function (#275).+ * Syntax rules (#277).+ * Remove unused module Ultrametric (#278).+ * Remove unused module CausalCommutative (#279).++2021-10-21+ * Version bump (0.8.1).+ * Code cleaning.+ * Update example in README to new API.+ * Fix broken link in README.+ * Update badge in README.+ * Fix typo in README.+ * Thanks: @DireWolf707.++2021-09-06+ * Version bump (0.8.0).+ * Code cleaning.+ * List GHC 8.10 as supported in README.+ * Thanks: @walseb, @turion, @bgluzman, @mezzomondo.++2020-04-18+ * Version bump (0.7.0).+ * Add new utilities.+ * Fix behaviour of listToMaybeS.+ * List GHC 8.8 as supported in README.+ * Cleaning and hlinting changes.+ * Thanks: @turion, @Linearity.++2020-01-25+ * Version bump (0.6.0).+ * Use simple-affine-space as dependency.+ * Remove Data.VectorSpace.+ * Fix error in documentation of embed.+ * List GHC 8.6 as supported in README.+ * Add links to videos to README.+ * Fix typos in README.+ * Thanks: @chriz-keera, @turion, @Rizary, @dynomite567,+ @alexmarginean16.++2019-09-29+ * Version bump (0.5.2.1).+ * Expose List support.+ * Fix error in List on older versions of GHC.++2019-09-29+ * Version bump (0.5.2).+ * Expose List support.+ * Fix error in List support implementation.++2018-11-03+ * Version bump (0.5.1).+ * Fix failed patterns in do notation.+ * Thanks: @turion, @ggreif.++2018-11-03+ * Version bump (0.5).+ * Rename lifting functions.+ * Move definitions around.+ * Thanks: @turion.++2017-12-05+ * Version bump (0.4).+ * Add support for random numbers using MonadRandom.+ * Remove MSF constructor from many definitions.+ * Add support for older GHC versions.+ * Move some definitions/modules around.+ * Change licenses in examples, update them to use new API.+ * Thanks: @thalerjonathan, @turion.++2017-10-31+ * Version bump (0.3).+ * Add repo URL to cabal file.+ * Add documentation to cabal file.+ * Re-structure modules.+ * Complete documentation in code.+ * Add stack configuration file.+ * Fix compilation errors with older versions of GHC.+ * Update examples to use new API.++Copyright (c) 2016, Ivan Perez and Manuel Bärenz.+All rights reserved.
+ benchmarks/Bench.hs view
@@ -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+ }
dunai.cabal view
@@ -1,14 +1,53 @@-name: dunai-version: 0.7.0-synopsis: Generalised reactive framework supporting classic, arrowized and monadic FRP.-homepage: https://github.com/ivanperez-keera/dunai+-- Copyright (c) 2016 - Ivan Perez and Manuel Bärenz - All rights reserved+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are met:+--+-- * Redistributions of source code must retain the above copyright+-- notice, this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above+-- copyright notice, this list of conditions and the following+-- disclaimer in the documentation and/or other materials provided+-- with the distribution.+--+-- * Neither the name of Ivan Perez and Manuel Bärenz nor the names of+-- other contributors may be used to endorse or promote products derived+-- from this software without specific prior written permission.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+cabal-version: >= 1.10+build-type: Simple++name: dunai+version: 0.14.2+author: Ivan Perez, Manuel Bärenz+maintainer: contact@ivanperez.io+homepage: https://github.com/ivanperez-keera/dunai+license: BSD3+license-file: LICENSE+copyright: Copyright (c) 2016 - Ivan Perez and Manuel Bärenz+category: Reactivity, FRP+synopsis:+ Generalised reactive framework supporting classic, arrowized and monadic FRP. description: Dunai is a DSL for strongly-typed CPS-based composable transformations. . Dunai is based on a concept of Monadic Stream Functions (MSFs). MSFs are- transformations defined by a function @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@- that executes one step of a simulation, and produces an output in a monadic- context, and a continuation to be used for future steps.+ transformations defined by a function+ @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@ that executes one step of a+ simulation, and produces an output in a monadic context, and a continuation+ to be used for future steps. . MSFs are a generalisation of the implementation mechanism used by Yampa, Wormholes and other FRP and reactive implementations.@@ -25,115 +64,185 @@ with @ListT@ gives rise to parallelism with broadcasting. . MSFs can be used to implement many FRP variants, including Arrowized FRP,- Classic FRP, and plain reactive programming. Arrowized and applicative- syntax are both supported.+ Classic FRP, and plain reactive programming. Arrowized and applicative syntax+ are both supported. . For a very detailed introduction to MSFs, see: <http://dl.acm.org/citation.cfm?id=2976010>- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).-license: BSD3-license-file: LICENSE-author: Ivan Perez, Manuel Bärenz-maintainer: ivan.perez@keera.co.uk--- copyright:-category: Reactivity, FRP-build-type: Simple--- extra-source-files:-cabal-version: >=1.10+ (mirror: <http://ivanperez.io/#FRPRefactored>). ++x-curation: uncurated+++extra-source-files:+ CHANGELOG+++source-repository head+ type: git+ location: https://github.com/ivanperez-keera/dunai.git+ subdir: dunai++ -- You can disable the hlint test suite with -f-test-hlint flag test-hlint description: Enable hlint test suite- default: False- manual: True+ default: False+ manual: True -- You can disable the haddock coverage test suite with -f-test-doc-coverage flag test-doc-coverage description: Enable haddock coverage test suite- default: False- manual: True+ default: False+ manual: True + library- exposed-modules: Control.Monad.Trans.MSF- Control.Monad.Trans.MSF.Except- Control.Monad.Trans.MSF.Maybe- Control.Monad.Trans.MSF.List- Control.Monad.Trans.MSF.Random- Control.Monad.Trans.MSF.Reader- Control.Monad.Trans.MSF.RWS- Control.Monad.Trans.MSF.State- Control.Monad.Trans.MSF.Writer- Data.MonadicStreamFunction- Data.MonadicStreamFunction.Core- Data.MonadicStreamFunction.Async- Data.MonadicStreamFunction.Instances.ArrowChoice- Data.MonadicStreamFunction.Instances.ArrowLoop- Data.MonadicStreamFunction.Instances.ArrowPlus- Data.MonadicStreamFunction.Instances.Num- Data.MonadicStreamFunction.Instances.VectorSpace- Data.MonadicStreamFunction.InternalCore- Data.MonadicStreamFunction.Parallel- Data.MonadicStreamFunction.ReactHandle- Data.MonadicStreamFunction.Util+ exposed-modules:+ Control.Monad.Trans.MSF+ Control.Monad.Trans.MSF.Except+ Control.Monad.Trans.MSF.List+ Control.Monad.Trans.MSF.Maybe+ Control.Monad.Trans.MSF.Random+ Control.Monad.Trans.MSF.Reader+ Control.Monad.Trans.MSF.RWS+ Control.Monad.Trans.MSF.State+ Control.Monad.Trans.MSF.Writer+ Data.MonadicStreamFunction+ Data.MonadicStreamFunction.Async+ Data.MonadicStreamFunction.Core+ Data.MonadicStreamFunction.Instances.ArrowChoice+ Data.MonadicStreamFunction.Instances.ArrowLoop+ Data.MonadicStreamFunction.Instances.ArrowPlus+ Data.MonadicStreamFunction.Instances.Num+ Data.MonadicStreamFunction.Instances.VectorSpace+ Data.MonadicStreamFunction.InternalCore+ Data.MonadicStreamFunction.Parallel+ Data.MonadicStreamFunction.ReactHandle+ Data.MonadicStreamFunction.Util - other-modules: Control.Arrow.Util+ other-modules:+ Control.Arrow.Util - build-depends: base >=4.6 && < 5,- transformers,- transformers-base,- MonadRandom,- simple-affine-space+ build-depends:+ base >= 4.6 && < 5+ , MonadRandom >= 0.2 && < 0.7+ , list-transformer >= 1.1.1 && < 1.2+ , simple-affine-space >= 0.2 && < 0.3+ , transformers >= 0.3 && < 0.7+ , transformers-base >= 0.4 && < 0.5 - hs-source-dirs: src- default-language: Haskell2010- ghc-options: -Wall -fno-warn-unused-do-bind+ default-language:+ Haskell2010 + hs-source-dirs:+ src++ ghc-options:+ -Wall -fno-warn-unused-do-bind+ if impl(ghc <= 7.8.4)- build-depends: transformers-compat+ build-depends:+ MonadRandom >= 0.2 && < 0.6+ , transformers-compat >= 0.3 && < 0.9+ , void >= 0.1 && < 0.8 + if impl(ghc < 8.0)+ build-depends:+ MonadRandom >= 0.2 && < 0.6.2.1+ test-suite hlint- type: exitcode-stdio-1.0- main-is: HLintMain.hs- hs-source-dirs: tests- default-language: Haskell2010+ type:+ exitcode-stdio-1.0++ main-is:+ HLintMain.hs++ default-language:+ Haskell2010++ hs-source-dirs:+ tests+ if !flag(test-hlint)- buildable: False+ buildable:+ False else build-depends:- base,- hlint >= 1.7+ base >= 4 && < 5+ , hlint >= 1.7 && < 3.11 -- Verify that the code is thoroughly documented test-suite haddock-coverage- type: exitcode-stdio-1.0- main-is: HaddockCoverage.hs- ghc-options: -Wall- hs-source-dirs: tests- default-language: Haskell2010+ type:+ exitcode-stdio-1.0 + main-is:+ HaddockCoverage.hs++ default-language:+ Haskell2010++ hs-source-dirs:+ tests++ ghc-options:+ -Wall+ if !flag(test-doc-coverage)- buildable: False+ buildable:+ False else build-depends:- base >= 4 && < 5,- directory,- filepath,- process,- regex-posix+ base >= 4 && < 5+ , directory >= 1.2 && < 1.4+ , filepath >= 1.3.0.1 && < 1.6+ , process >= 1.1.0.2 && < 1.7+ , regex-posix >= 0.95 && < 0.97 -source-repository head- type: git- location: git@github.com:ivanperez-keera/dunai.git test-suite regression-tests- type: exitcode-stdio-1.0- main-is: RegressionTests.hs- ghc-options: -Wall- hs-source-dirs: tests- default-language: Haskell2010+ type:+ exitcode-stdio-1.0++ main-is:+ RegressionTests.hs++ default-language:+ Haskell2010++ hs-source-dirs:+ tests++ ghc-options:+ -Wall+ build-depends:- base >=4 && <5,- transformers,- dunai,- tasty,- tasty-hunit+ base >= 4 && < 5+ , transformers >= 0.3 && < 0.7+ , tasty >= 0.1 && < 1.6+ , tasty-hunit >= 0.8 && < 0.11++ , dunai+++benchmark dunai-bench+ type:+ exitcode-stdio-1.0++ main-is:+ Bench.hs++ build-depends:+ base >= 4 && < 5+ , criterion >= 0.5.0.0 && < 1.7+ , filepath >= 1.3.0.1 && < 1.6+ , time >= 1.4 && < 1.17+ , dunai++ default-language:+ Haskell2010++ hs-source-dirs:+ benchmarks
src/Control/Arrow/Util.hs view
@@ -1,24 +1,21 @@--- | Utility functions to work with 'Arrow's.+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- Utility functions to work with 'Arrow's. module Control.Arrow.Util where -import Control.Arrow+-- External imports+import Control.Arrow (Arrow, arr, (&&&), (>>^), (^<<)) -- | Constantly produce the same output. constantly :: Arrow a => b -> a c b constantly = arr . const {-# INLINE constantly #-} --- import Control.Category (id)--- import Prelude hiding (id)---- (&&&!) :: Arrow a => a b c -> a b () -> a b c--- a1 &&&! a2 = (a1 &&& a2) >>> arr fst---- sink :: Arrow a => a b c -> a c () -> a b c--- a1 `sink` a2 = a1 >>> (id &&& a2) >>> arr fst+-- * Apply functions at the end --- * Apply functions at the end.--- -- | Alternative name for '^<<'. elementwise :: Arrow a => (c -> d) -> a b c -> a b d elementwise = (^<<)
src/Control/Monad/Trans/MSF.hs view
@@ -1,6 +1,9 @@-{-# LANGUAGE Rank2Types #-}---- | This module reexports nearly all submodules. RWS is not exported since+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- This module reexports nearly all submodules. RWS is not exported since -- names collide with Reader, State and Writer. module Control.Monad.Trans.MSF ( module Control.Monad.Trans.MSF.Except@@ -12,6 +15,7 @@ ) where +-- Internal imports import Control.Monad.Trans.MSF.Except import Control.Monad.Trans.MSF.Maybe import Control.Monad.Trans.MSF.Random
src/Control/Monad/Trans/MSF/Except.hs view
@@ -1,32 +1,43 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE TupleSections #-}--- | 'MSF's in the 'ExceptT' monad are monadic stream functions--- that can throw exceptions,--- i.e. return an exception value instead of a continuation.--- This module gives ways to throw exceptions in various ways,--- and to handle them through a monadic interface.+{-# LANGUAGE Arrows #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE Rank2Types #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- 'MSF's in the 'ExceptT' monad are monadic stream functions that can throw+-- exceptions, i.e. return an exception value instead of a continuation. This+-- module gives ways to throw exceptions in various ways, and to handle them+-- through a monadic interface. module Control.Monad.Trans.MSF.Except- ( module Control.Monad.Trans.MSF.Except- , module Control.Monad.Trans.Except- ) where+ ( module Control.Monad.Trans.MSF.Except+ , module Control.Monad.Trans.Except+ )+ where --- External+-- External imports+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative (Applicative (..), (<$>))+#endif -import Control.Applicative+import Control.Arrow (arr, returnA, (<<<), (>>>)) import qualified Control.Category as Category-import Control.Monad (liftM, ap)-import Control.Monad.Trans.Class-import Control.Monad.Trans.Except hiding (liftCallCC, liftListen, liftPass) -- Avoid conflicting exports-import Control.Monad.Trans.Maybe+import Control.Monad (ap, liftM)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Except hiding (liftCallCC, liftListen,+ liftPass)+import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)+import Data.Void (Void) --- Internal-import Data.MonadicStreamFunction-import Data.MonadicStreamFunction.InternalCore+-- Internal imports+import Data.MonadicStreamFunction (arrM, constM, count, feedback,+ liftTransS, mapMaybeS, morphS,+ reactimate)+import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) -- External, necessary for older base versions-#if __GLASGOW_HASKELL__ < 802+#if !MIN_VERSION_base(4,10,0) fromLeft :: a -> Either a b -> a fromLeft _ (Left a) = a fromLeft a (Right _) = a@@ -45,8 +56,8 @@ then throwS -< e else returnA -< a --- | Variant of 'throwOnCond' for Kleisli arrows.--- | Throws the exception when the input is 'True'.+-- | Throws the exception when the input is 'True'. Variant of 'throwOnCond'+-- for Kleisli arrows. throwOnCondM :: Monad m => (a -> m Bool) -> e -> MSF (ExceptT e m) a a throwOnCondM cond e = proc a -> do b <- arrM (lift . cond) -< a@@ -64,8 +75,8 @@ then throwS -< e else returnA -< () --- | When the input is @Just e@, throw the exception @e@.--- (Does not output any actual data.)+-- | When the input is @Just e@, throw the exception @e@. (Does not output any+-- actual data.) throwMaybe :: Monad m => MSF (ExceptT e m) (Maybe e) (Maybe a) throwMaybe = mapMaybeS throwS @@ -81,78 +92,74 @@ pass :: Monad m => MSF (ExceptT e m) a a pass = Category.id --- | Converts an 'MSF' in 'MaybeT' to an 'MSF' in 'ExceptT'.--- Whenever 'Nothing' is thrown, throw @()@ instead.+-- | Converts an 'MSF' in 'MaybeT' to an 'MSF' in 'ExceptT'. Whenever+-- 'Nothing' is thrown, throw @()@ instead. maybeToExceptS :: (Functor m, Monad m) => MSF (MaybeT m) a b -> MSF (ExceptT () m) a b maybeToExceptS = morphS (ExceptT . (maybe (Left ()) Right <$>) . runMaybeT) -- * Catching exceptions --- | Catch an exception in an 'MSF'. As soon as an exception occurs,--- the current continuation is replaced by a new 'MSF', the exception handler,--- based on the exception value.--- For exception catching where the handler can throw further exceptions,--- see 'MSFExcept' further below.+-- | Catch an exception in an 'MSF'. As soon as an exception occurs, the+-- current continuation is replaced by a new 'MSF', the exception handler,+-- based on the exception value. For exception catching where the handler can+-- throw further exceptions, see 'MSFExcept' further below. catchS :: Monad m => MSF (ExceptT e m) a b -> (e -> MSF m a b) -> MSF m a b catchS msf f = safely $ do e <- try msf safe $ f e -- | Similar to Yampa's delayed switching. Loses a @b@ in case of an exception.-untilE :: Monad m => MSF m a b -> MSF m b (Maybe e)+untilE :: Monad m+ => MSF m a b+ -> MSF m b (Maybe e) -> MSF (ExceptT e m) a b untilE msf msfe = proc a -> do b <- liftTransS msf -< a me <- liftTransS msfe -< b inExceptT -< ExceptT $ return $ maybe (Right b) Left me --- TODO This needs to be renamed as 'runExceptS'!--- 'exceptS' would have type @MSF m a (Either e b) -> MSF (ExceptT e m) a b@ -- | Escape an 'ExceptT' layer by outputting the exception whenever it occurs.--- If an exception occurs, the current 'MSF' continuation is tested again--- on the next input.+-- If an exception occurs, the current 'MSF' continuation is tested again on+-- the next input. exceptS :: (Functor m, Monad m) => MSF (ExceptT e m) a b -> MSF m a (Either e b) exceptS = transG return $ const $ fmap f . runExceptT where f (Left e) = (Left e , Nothing) f (Right (b, c)) = (Right b, Just c ) --- | Embed an 'ExceptT' value inside the 'MSF'.--- Whenever the input value is an ordinary value,--- it is passed on. If it is an exception, it is raised.+-- | Embed an 'ExceptT' value inside the 'MSF'. Whenever the input value is an+-- ordinary value, it is passed on. If it is an exception, it is raised. inExceptT :: Monad m => MSF (ExceptT e m) (ExceptT e m a) a inExceptT = arrM id --- | In case an exception occurs in the first argument,--- replace the exception by the second component of the tuple.+-- | In case an exception occurs in the first argument, replace the exception+-- by the second component of the tuple. tagged :: Monad m => MSF (ExceptT e1 m) a b -> MSF (ExceptT e2 m) (a, e2) b tagged msf = runMSFExcept $ do _ <- try $ msf <<< arr fst (_, e2) <- currentInput return e2 - -- * Monad interface for Exception MSFs --- | 'MSF's with an 'ExceptT' transformer layer--- are in fact monads /in the exception type/.+-- | 'MSF's with an 'ExceptT' transformer layer are in fact monads /in the+-- exception type/. -- -- * 'return' corresponds to throwing an exception immediately.--- * '>>=' is exception handling:--- The first value throws an exception,--- while the Kleisli arrow handles the exception--- and produces a new signal function,--- which can throw exceptions in a different type.+-- * '>>=' is exception handling: The first value throws an exception, while+-- the Kleisli arrow handles the exception and produces a new signal+-- function, which can throw exceptions in a different type. -- * @m@: The monad that the 'MSF' may take side effects in. -- * @a@: The input type -- * @b@: The output type -- * @e@: The type of exceptions that can be thrown newtype MSFExcept m a b e = MSFExcept { runMSFExcept :: MSF (ExceptT e m) a b } --- | An alias for the 'MSFExcept' constructor,--- used to enter the 'MSFExcept' monad context.--- Execute an 'MSF' in 'ExceptT' until it raises an exception.+-- | Execute an 'MSF' in 'ExceptT' until it raises an exception.+--+-- An alias for the 'MSFExcept' constructor, used to enter the 'MSFExcept'+-- monad context. try :: MSF (ExceptT e m) a b -> MSFExcept m a b e try = MSFExcept @@ -177,40 +184,36 @@ return = pure MSFExcept msf >>= f = MSFExcept $ handleExceptT msf $ runMSFExcept . f -handleExceptT- :: Monad m- => MSF (ExceptT e1 m) a b- -> (e1 -> MSF (ExceptT e2 m) a b)- -> MSF (ExceptT e2 m) a b+-- | Execute an MSF and, if it throws an exception, recover by switching to a+-- second MSF.+handleExceptT :: Monad m+ => MSF (ExceptT e1 m) a b+ -> (e1 -> MSF (ExceptT e2 m) a b)+ -> MSF (ExceptT e2 m) a b handleExceptT msf f = flip handleGen msf $ \a mbcont -> do ebcont <- lift $ runExceptT mbcont case ebcont of Left e -> unMSF (f e) a Right (b, msf') -> return (b, handleExceptT msf' f) ----- | The empty type. As an exception type, it encodes "no exception possible".-data Empty---- | If no exception can occur, the 'MSF' can be executed without the 'ExceptT' layer.-safely :: Monad m => MSFExcept m a b Empty -> MSF m a b+-- | If no exception can occur, the 'MSF' can be executed without the 'ExceptT'+-- layer.+safely :: Monad m => MSFExcept m a b Void -> MSF m a b safely (MSFExcept msf) = morphS fromExcept msf where- -- We can assume that the pattern @Left e@ will not occur,- -- since @e@ would have to be of type @Empty@.+ -- We can assume that the pattern @Left e@ will not occur, since @e@ would+ -- have to be of type @Void@. fromExcept ma = do rightMa <- runExceptT ma return $ fromRight (error "safely: Received `Left`") rightMa --- | An 'MSF' without an 'ExceptT' layer never throws an exception,--- and can thus have an arbitrary exception type.+-- | An 'MSF' without an 'ExceptT' layer never throws an exception, and can+-- thus have an arbitrary exception type. safe :: Monad m => MSF m a b -> MSFExcept m a b e safe = try . liftTransS -- | Inside the 'MSFExcept' monad, execute an action of the wrapped monad.--- This passes the last input value to the action,--- but doesn't advance a tick.+-- This passes the last input value to the action, but doesn't advance a tick. once :: Monad m => (a -> m e) -> MSFExcept m a b e once f = try $ arrM (lift . f) >>> throwS @@ -218,8 +221,8 @@ once_ :: Monad m => m e -> MSFExcept m a b e once_ = once . const --- | Advances a single tick with the given Kleisli arrow,--- and then throws an exception.+-- | Advances a single tick with the given Kleisli arrow, and then throws an+-- exception. step :: Monad m => (a -> m (b, e)) -> MSFExcept m a b e step f = try $ proc a -> do n <- count -< ()@@ -227,29 +230,22 @@ _ <- throwOn' -< (n > (1 :: Int), e) returnA -< b --- | Advances a single tick outputting the value,--- and then throws '()'.+-- | Advances a single tick outputting the value, and then throws '()'. step_ :: Monad m => b -> MSFExcept m a b ()-step_ = step . const . return . (, ())+step_ b = step $ const $ return (b, ()) --- | Converts a list to an 'MSFExcept',--- which outputs an element of the list at each step,--- throwing '()' when the list ends.+-- | Converts a list to an 'MSFExcept', which outputs an element of the list at+-- each step, throwing '()' when the list ends. listToMSFExcept :: Monad m => [b] -> MSFExcept m a b () listToMSFExcept = mapM_ step_ -- * Utilities definable in terms of 'MSFExcept' --- TODO This is possibly not the best location for these functions,--- but moving them to Data.MonadicStreamFunction.Util would form an import cycle--- that could only be broken by moving a few things to Data.MonadicStreamFunction.Core--- (that probably belong there anyways).- -- | Extract an 'MSF' from a monadic action. ----- Runs a monadic action that produces an 'MSF' on the first iteration/step, and--- uses that 'MSF' as the main signal function for all inputs (including the--- first one).+-- Runs a monadic action that produces an 'MSF' on the first iteration/step,+-- and uses that 'MSF' as the main signal function for all inputs (including+-- the first one). performOnFirstSample :: Monad m => m (MSF m a b) -> MSF m a b performOnFirstSample sfaction = safely $ do msf <- once_ sfaction@@ -265,14 +261,35 @@ reactimateB :: Monad m => MSF m () Bool -> m () reactimateB sf = reactimateExcept $ try $ liftTransS sf >>> throwOn () --- * Analog to Yampa's switch, with Maybe instead of Event+-- | Run first MSF until the second value in the output tuple is @Just c@ (for+-- some @c@), then start the second MSF.+--+-- Analog to Yampa's+-- [@switch@](https://hackage.haskell.org/package/Yampa/docs/FRP-Yampa-Switches.html#v:switch),+-- with 'Maybe' instead of @Event@. switch :: Monad m => MSF m a (b, Maybe c) -> (c -> MSF m a b) -> MSF m a b-switch sf f = catchS ef f+switch sf f = catchS ef f where+ -- Run sf, throwing an exception if there is a no-Nothing value in the+ -- second element of the pair, and returning the first element otherwise. ef = proc a -> do- (b,me) <- liftTransS sf -< a- inExceptT -< ExceptT $ return $ maybe (Right b) Left me+ (b, me) <- liftTransS sf -< a+ throwMaybe -< me+ returnA -< b +-- | Run first MSF until the second value in the output tuple is @Just c@ (for+-- some @c@), then start the second MSF.+--+-- Analog to Yampa's+-- [@dswitch@](https://hackage.haskell.org/package/Yampa/docs/FRP-Yampa-Switches.html#v:dSwitch),+-- with 'Maybe' instead of @Event@.+dSwitch :: Monad m => MSF m a (b, Maybe c) -> (c -> MSF m a b) -> MSF m a b+dSwitch sf f = catchS ef f+ where+ ef = feedback Nothing $ proc (a, me) -> do+ throwMaybe -< me+ liftTransS sf -< a+ -- | More general lifting combinator that enables recovery. Note that, unlike a -- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary -- function needs to be a bit more structured, and produces a Maybe value. The@@ -283,12 +300,16 @@ -> MSF m1 a1 b1 -> MSF m2 a2 b2 transG transformInput transformOutput msf = go- where go = MSF $ \a2 -> do- (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2- case msf' of- Just msf'' -> return (b2, transG transformInput transformOutput msf'')- Nothing -> return (b2, go)+ where+ go = MSF $ \a2 -> do+ (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2+ case msf' of+ Just msf'' ->+ return (b2, transG transformInput transformOutput msf'')+ Nothing ->+ return (b2, go) +-- | Use a generic handler to handle exceptions in MSF processing actions. handleGen :: (a -> m1 (b1, MSF m1 a b1) -> m2 (b2, MSF m2 a b2)) -> MSF m1 a b1 -> MSF m2 a b2
src/Control/Monad/Trans/MSF/List.hs view
@@ -1,39 +1,68 @@ {-# LANGUAGE CPP #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- 'MSF's with a list monadic layer.+--+-- This module contains functions to work with MSFs that include a 'ListT'+-- monadic layer. MSFs on a list monad may produce multiple outputs and+-- continuations, or none. This enables the possibility for spawning new MSFs,+-- or stopping MSFs, at will.+--+-- A common use case is to be able to dynamically spawn new interactive+-- elements in applications (e.g., a game object that splits in two, or that+-- fires to an enemy). module Control.Monad.Trans.MSF.List- ( module Control.Monad.Trans.MSF.List- , module Control.Monad.Trans.List- ) where---- External-import Control.Monad.Trans.List- hiding (liftCallCC, liftCatch) -- Avoid conflicting exports+ ( module Control.Monad.Trans.MSF.List+ , module List+ )+ where +-- External imports #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) #endif --- Internal-import Data.MonadicStreamFunction+import Control.Monad (sequence)+import List.Transformer (ListT (ListT, next), Step (..), fold, select)+import qualified List.Transformer as List++-- Internal imports import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) -- * List monad --- Name alternative (in the article): collect+-- | Run an 'MSF' in the 'ListT' transformer (i.e., multiple MSFs producing+-- each producing one output), by applying the input stream to each MSF in the+-- list transformer and concatenating the outputs of the MSFs together.+--+-- An MSF in the ListT transformer can spawn into more than one MSF, or none,+-- so the outputs produced at each individual step are not guaranteed to all+-- have the same length. widthFirst :: (Functor m, Monad m) => MSF (ListT m) a b -> MSF m a [b]-widthFirst msf = widthFirst' [msf] where+widthFirst msf = widthFirst' [msf]+ where widthFirst' msfs = MSF $ \a -> do- (bs, msfs') <- unzip . concat <$> mapM (runListT . flip unMSF a) msfs- return (bs, widthFirst' msfs')+ (bs, msfs') <- unzip . concat <$> mapM (toList . flip unMSF a) msfs+ return (bs, widthFirst' msfs') + toList :: (Functor m, Monad m) => ListT m a -> m [a]+ toList = fmap reverse . fold (flip (:)) [] id --- Name alternatives: "choose", "parallely" (problematic because it's not multicore)+-- | Build an 'MSF' in the 'ListT' transformer by broadcasting the input stream+-- value to each MSF in a given list. sequenceS :: Monad m => [MSF m a b] -> MSF (ListT m) a b-sequenceS msfs = MSF $ \a -> ListT $ sequence $ apply a <$> msfs+sequenceS msfs = MSF $ \a -> sequence' $ apply a <$> msfs where+ sequence' :: Monad m => [m a] -> ListT m a+ sequence' xs = ListT $ next <$> select =<< sequence xs++ apply :: Monad m => a -> MSF m a b -> m (b, MSF (ListT m) a b) apply a msf = do- (b, msf') <- unMSF msf a- return (b, sequenceS [msf'])--- sequenceS = foldl (<+>) arrowzero . map liftMSFTrans+ (b, msf') <- unMSF msf a+ return (b, sequenceS [msf']) -- | Apply an 'MSF' to every input. mapMSF :: Monad m => MSF m a b -> MSF m [a] [b]@@ -45,4 +74,3 @@ (b, sf') <- unMSF sf a (bs, sf'') <- consume sf' as b `seq` return (b:bs, sf'')-
src/Control/Monad/Trans/MSF/Maybe.hs view
@@ -1,23 +1,30 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE Rank2Types #-}-{- |-The 'Maybe' monad is very versatile. It can stand for default arguments,-for absent values, and for (nondescript) exceptions.-The latter viewpoint is most natural in the context of 'MSF's.--}+{-# LANGUAGE Arrows #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- The 'Maybe' monad is very versatile. It can stand for default arguments,+-- for absent values, and for (nondescript) exceptions. The latter viewpoint+-- is most natural in the context of 'MSF's. module Control.Monad.Trans.MSF.Maybe- ( module Control.Monad.Trans.MSF.Maybe- , module Control.Monad.Trans.Maybe- , maybeToExceptS- ) where+ ( module Control.Monad.Trans.MSF.Maybe+ , module Control.Monad.Trans.Maybe+ , maybeToExceptS+ )+ where --- External-import Control.Monad.Trans.Maybe- hiding (liftCallCC, liftCatch, liftListen, liftPass) -- Avoid conflicting exports+-- External imports+import Control.Arrow (returnA, (>>>), arr)+import Control.Monad.Trans.Maybe hiding (liftCallCC, liftCatch, liftListen,+ liftPass) --- Internal-import Control.Monad.Trans.MSF.Except-import Data.MonadicStreamFunction+-- Internal imports+import Control.Monad.Trans.MSF.Except (ExceptT, exceptS, listToMSFExcept,+ maybeToExceptS, reactimateExcept,+ runExceptT, runMSFExcept, safe, safely,+ try)+import Data.MonadicStreamFunction (MSF, arrM, constM, liftTransS, morphS) -- * Throwing 'Nothing' as an exception ("exiting") @@ -45,20 +52,22 @@ inMaybeT :: Monad m => MSF (MaybeT m) (Maybe a) a inMaybeT = arrM $ MaybeT . return - -- * Catching Maybe exceptions --- | Run the first @msf@ until the second one produces 'True' from the output of the first.+-- | Run the first @msf@ until the second one produces 'True' from the output+-- of the first. untilMaybe :: Monad m => MSF m a b -> MSF m b Bool -> MSF (MaybeT m) a b untilMaybe msf cond = proc a -> do b <- liftTransS msf -< a c <- liftTransS cond -< b inMaybeT -< if c then Nothing else Just b --- | When an exception occurs in the first 'msf', the second 'msf' is executed from there.-catchMaybe- :: (Functor m, Monad m)- => MSF (MaybeT m) a b -> MSF m a b -> MSF m a b+-- | When an exception occurs in the first 'msf', the second 'msf' is executed+-- from there.+catchMaybe :: (Functor m, Monad m)+ => MSF (MaybeT m) a b+ -> MSF m a b+ -> MSF m a b catchMaybe msf1 msf2 = safely $ do _ <- try $ maybeToExceptS msf1 safe msf2@@ -66,33 +75,35 @@ -- * Converting to and from 'MaybeT' -- | Convert exceptions into `Nothing`, discarding the exception value.-exceptToMaybeS :: (Functor m, Monad m) => MSF (ExceptT e m) a b -> MSF (MaybeT m) a b-exceptToMaybeS = morphS $ MaybeT . fmap (either (const Nothing) Just) . runExceptT+exceptToMaybeS :: (Functor m, Monad m)+ => MSF (ExceptT e m) a b+ -> MSF (MaybeT m) a b+exceptToMaybeS =+ morphS $ MaybeT . fmap (either (const Nothing) Just) . runExceptT --- | Converts a list to an 'MSF' in 'MaybeT',--- which outputs an element of the list at each step,--- throwing 'Nothing' when the list ends.+-- | Converts a list to an 'MSF' in 'MaybeT', which outputs an element of the+-- list at each step, throwing 'Nothing' when the list ends. listToMaybeS :: (Functor m, Monad m) => [b] -> MSF (MaybeT m) a b listToMaybeS = exceptToMaybeS . runMSFExcept . listToMSFExcept -- * Running 'MaybeT'--- | Remove the 'MaybeT' layer by outputting 'Nothing' when the exception occurs.--- The continuation in which the exception occurred is then tested on the next input.++-- | Remove the 'MaybeT' layer by outputting 'Nothing' when the exception+-- occurs. The continuation in which the exception occurred is then tested on+-- the next input. runMaybeS :: (Functor m, Monad m) => MSF (MaybeT m) a b -> MSF m a (Maybe b) runMaybeS msf = exceptS (maybeToExceptS msf) >>> arr eitherToMaybe where eitherToMaybe (Left ()) = Nothing eitherToMaybe (Right b) = Just b - -- | Reactimates an 'MSF' in the 'MaybeT' monad until it throws 'Nothing'.-reactimateMaybe- :: (Functor m, Monad m)- => MSF (MaybeT m) () () -> m ()+reactimateMaybe :: (Functor m, Monad m)+ => MSF (MaybeT m) () ()+ -> m () reactimateMaybe msf = reactimateExcept $ try $ maybeToExceptS msf -- | Run an 'MSF' fed from a list, discarding results. Useful when one needs to -- combine effects and streams (i.e., for testing purposes). embed_ :: (Functor m, Monad m) => MSF m a () -> [a] -> m ()- embed_ msf as = reactimateMaybe $ listToMaybeS as >>> liftTransS msf
src/Control/Monad/Trans/MSF/RWS.hs view
@@ -1,26 +1,34 @@--- | This module combines the wrapping and running functions--- for the 'Reader', 'Writer' and 'State' monad layers in a single layer.+{-# LANGUAGE CPP #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- This module combines the wrapping and running functions for the 'Reader',+-- 'Writer' and 'State' monad layers in a single layer.+-- -- It is based on the _strict_ 'RWS' monad 'Control.Monad.Trans.RWS.Strict',--- so when combining it with other modules such as @mtl@'s,--- the strict version has to be included, i.e. 'Control.Monad.RWS.Strict'--- instead of 'Control.Monad.RWS' or 'Control.Monad.RWS.Lazy'.+-- so when combining it with other modules such as @mtl@'s, the strict version+-- has to be included, i.e. 'Control.Monad.RWS.Strict' instead of+-- 'Control.Monad.RWS' or 'Control.Monad.RWS.Lazy'. module Control.Monad.Trans.MSF.RWS- ( module Control.Monad.Trans.MSF.RWS- , module Control.Monad.Trans.RWS.Strict- ) where+ ( module Control.Monad.Trans.MSF.RWS+ , module Control.Monad.Trans.RWS.Strict+ )+ where --- External-import Control.Monad.Trans.RWS.Strict- hiding (liftCallCC, liftCatch) -- Avoid conflicting exports-import Data.Monoid+-- External imports+import Control.Monad.Trans.RWS.Strict hiding (liftCallCC, liftCatch)++#if !MIN_VERSION_base(4,8,0) import Data.Functor ((<$>))+import Data.Monoid (Monoid)+#endif --- Internal-import Data.MonadicStreamFunction+-- Internal imports+import Data.MonadicStreamFunction (MSF, morphGS) -- * 'RWS' (Reader-Writer-State) monad- -- | Wrap an 'MSF' with explicit state variables in 'RWST' monad. rwsS :: (Functor m, Monad m, Monoid w)
src/Control/Monad/Trans/MSF/Random.hs view
@@ -1,47 +1,47 @@--- | In this module, 'MSF's in a monad supporting random number generation--- (i.e. having the 'RandT' layer in its stack) can be run.--- Running means supplying an initial random number generator,--- where the update of the generator at every random number generation--- is already taken care of.+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Under the hood, 'RandT' is basically just 'StateT',--- with the current random number generator as mutable state.---{-# LANGUAGE Arrows #-}+-- In this module, 'MSF's in a monad supporting random number generation (i.e.+-- having the 'RandT' layer in its stack) can be run. Running means supplying+-- an initial random number generator, where the update of the generator at+-- every random number generation is already taken care of.+--+-- Under the hood, 'RandT' is basically just 'StateT', with the current random+-- number generator as mutable state. module Control.Monad.Trans.MSF.Random- (- runRandS- , evalRandS-- , getRandomS- , getRandomsS- , getRandomRS- , getRandomRS_- , getRandomsRS- , getRandomsRS_- ) where+ ( runRandS+ , evalRandS+ , getRandomS+ , getRandomsS+ , getRandomRS+ , getRandomRS_+ , getRandomsRS+ , getRandomsRS_+ )+ where --- External-import Control.Monad.Random+-- External imports+import Control.Arrow (arr, (>>>))+import Control.Monad.Random (MonadRandom, RandT, Random, RandomGen, getRandom,+ getRandomR, getRandomRs, getRandoms, runRandT) --- Internal-import Data.MonadicStreamFunction-import Control.Monad.Trans.MSF.State+-- Internal imports+import Control.Monad.Trans.MSF.State (StateT (..), runStateS_)+import Data.MonadicStreamFunction (MSF, arrM, constM, morphS) --- | Run an 'MSF' in the 'RandT' random number monad transformer--- by supplying an initial random generator.--- Updates the generator every step.+-- | Run an 'MSF' in the 'RandT' random number monad transformer by supplying+-- an initial random generator. Updates the generator every step. runRandS :: (RandomGen g, Functor m, Monad m) => MSF (RandT g m) a b -> g -- ^ The initial random number generator. -> MSF m a (g, b) runRandS = runStateS_ . morphS (StateT . runRandT) --- | Evaluate an 'MSF' in the 'RandT' transformer,--- i.e. extract possibly random values--- by supplying an initial random generator.--- Updates the generator every step but discharges the generator.+-- | Evaluate an 'MSF' in the 'RandT' transformer, i.e. extract possibly random+-- values by supplying an initial random generator. Updates the generator every+-- step but discharges the generator. evalRandS :: (RandomGen g, Functor m, Monad m) => MSF (RandT g m) a b -> g -> MSF m a b evalRandS msf g = runRandS msf g >>> arr snd@@ -50,7 +50,6 @@ getRandomS :: (MonadRandom m, Random b) => MSF m a b getRandomS = constM getRandom - -- | Create a stream of lists of random values. getRandomsS :: (MonadRandom m, Random b) => MSF m a [b] getRandomsS = constM getRandoms@@ -59,8 +58,8 @@ getRandomRS :: (MonadRandom m, Random b) => (b, b) -> MSF m a b getRandomRS range = constM $ getRandomR range --- | Create a stream of random values in a given range,--- where the range is specified on every tick.+-- | Create a stream of random values in a given range, where the range is+-- specified on every tick. getRandomRS_ :: (MonadRandom m, Random b) => MSF m (b, b) b getRandomRS_ = arrM getRandomR @@ -68,7 +67,7 @@ getRandomsRS :: (MonadRandom m, Random b) => (b, b) -> MSF m a [b] getRandomsRS range = constM $ getRandomRs range --- | Create a stream of lists of random values in a given range,--- where the range is specified on every tick.+-- | Create a stream of lists of random values in a given range, where the+-- range is specified on every tick. getRandomsRS_ :: (MonadRandom m, Random b) => MSF m (b, b) [b] getRandomsRS_ = arrM getRandomRs
src/Control/Monad/Trans/MSF/Reader.hs view
@@ -1,25 +1,29 @@-{-# LANGUAGE Rank2Types #-}---- | 'MSF's with a 'Reader' monadic layer.+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- 'MSF's with a 'Reader' monadic layer.+-- -- This module contains functions to work with 'MSF's that include a 'Reader' -- monadic layer. This includes functions to create new 'MSF's that include an -- additional layer, and functions to flatten that layer out of the 'MSF`'s -- transformer stack. module Control.Monad.Trans.MSF.Reader- ( module Control.Monad.Trans.Reader- -- * 'Reader' 'MSF' running and wrapping.- , readerS- , runReaderS- , runReaderS_- ) where+ ( module Control.Monad.Trans.Reader+ -- * 'Reader' 'MSF' running and wrapping.+ , readerS+ , runReaderS+ , runReaderS_+ )+ where --- External-import Control.Monad.Trans.Reader- hiding (liftCallCC, liftCatch) -- Avoid conflicting exports+-- External imports+import Control.Arrow (arr, (>>>))+import Control.Monad.Trans.Reader hiding (liftCallCC, liftCatch) --- Internal-import Data.MonadicStreamFunction+-- Internal imports+import Data.MonadicStreamFunction (MSF, morphGS) -- * Reader 'MSF' running and wrapping @@ -33,8 +37,7 @@ runReaderS :: Monad m => MSF (ReaderT r m) a b -> MSF m (r, a) b runReaderS = morphGS $ \f (r, a) -> runReaderT (f a) r - -- | Build an 'MSF' /function/ that takes a fixed environment as additional -- input, from an MSF in the 'Reader' monad. runReaderS_ :: Monad m => MSF (ReaderT s m) a b -> s -> MSF m a b-runReaderS_ msf s = arr (\a -> (s,a)) >>> runReaderS msf+runReaderS_ msf s = arr (\a -> (s, a)) >>> runReaderS msf
src/Control/Monad/Trans/MSF/State.hs view
@@ -1,6 +1,11 @@-{-# LANGUAGE Rank2Types #-}--- | 'MSF's with a 'State' monadic layer.+{-# LANGUAGE CPP #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- 'MSF's with a 'State' monadic layer.+-- -- This module contains functions to work with 'MSF's that include a 'State' -- monadic layer. This includes functions to create new 'MSF's that include an -- additional layer, and functions to flatten that layer out of the 'MSF`'s@@ -11,24 +16,28 @@ -- the strict version has to be included, i.e. 'Control.Monad.State.Strict' -- instead of 'Control.Monad.State' or 'Control.Monad.State.Lazy'. module Control.Monad.Trans.MSF.State- ( module Control.Monad.Trans.State.Strict- -- * 'State' 'MSF' running and wrapping- , stateS- , runStateS- , runStateS_- , runStateS__- ) where+ ( module Control.Monad.Trans.State.Strict+ -- * 'State' 'MSF' running and wrapping+ , stateS+ , runStateS+ , runStateS_+ , runStateS__+ )+ where --- External-import Control.Applicative-import Control.Monad.Trans.State.Strict- hiding (liftCallCC, liftCatch, liftListen, liftPass) -- Avoid conflicting exports-import Data.Tuple (swap)+-- External imports+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif --- Internal-import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.InternalCore+import Control.Arrow (arr, (>>>))+import Control.Monad.Trans.State.Strict hiding (liftCallCC, liftCatch,+ liftListen, liftPass)+import Data.Tuple (swap) +-- Internal imports+import Data.MonadicStreamFunction.Core (MSF, morphGS, feedback)+ -- * 'State' 'MSF' running and wrapping -- | Build an 'MSF' in the 'State' monad from one that takes the state as an@@ -46,12 +55,15 @@ -- | Build an 'MSF' /function/ that takes a fixed state as additional input, -- from an 'MSF' in the 'State' monad, and outputs the new state with every -- transformation step.-runStateS_ :: (Functor m, Monad m) => MSF (StateT s m) a b -> s -> MSF m a (s, b)-runStateS_ msf s = feedback s- $ arr swap >>> runStateS msf >>> arr (\(s', b) -> ((s', b), s'))+runStateS_ :: (Functor m, Monad m)+ => MSF (StateT s m) a b+ -> s+ -> MSF m a (s, b)+runStateS_ msf s =+ feedback s $+ arr swap >>> runStateS msf >>> arr (\(s', b) -> ((s', b), s')) --- TODO Rename this to execStateS!--- | Build an 'MSF' /function/ that takes a fixed state as additional--- input, from an 'MSF' in the 'State' monad.+-- | Build an 'MSF' /function/ that takes a fixed state as additional input,+-- from an 'MSF' in the 'State' monad. runStateS__ :: (Functor m, Monad m) => MSF (StateT s m) a b -> s -> MSF m a b runStateS__ msf s = runStateS_ msf s >>> arr snd
src/Control/Monad/Trans/MSF/Writer.hs view
@@ -1,5 +1,11 @@--- | 'MSF's with a 'Writer' monadic layer.+{-# LANGUAGE CPP #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- 'MSF's with a 'Writer' monadic layer.+-- -- This module contains functions to work with 'MSF's that include a 'Writer' -- monadic layer. This includes functions to create new 'MSF's that include an -- additional layer, and functions to flatten that layer out of the 'MSF`'s@@ -10,20 +16,23 @@ -- the strict version has to be included, i.e. 'Control.Monad.Writer.Strict' -- instead of 'Control.Monad.Writer' or 'Control.Monad.Writer.Lazy'. module Control.Monad.Trans.MSF.Writer- ( module Control.Monad.Trans.Writer.Strict- -- * 'Writer' 'MSF' running and wrapping- , writerS- , runWriterS- ) where+ ( module Control.Monad.Trans.Writer.Strict+ -- * 'Writer' 'MSF' running and wrapping+ , writerS+ , runWriterS+ )+ where --- External-import Control.Monad.Trans.Writer.Strict- hiding (liftCallCC, liftCatch, pass) -- Avoid conflicting exports+-- External imports+import Control.Monad.Trans.Writer.Strict hiding (liftCallCC, liftCatch, pass)++#if !MIN_VERSION_base(4,8,0) import Data.Functor ((<$>))-import Data.Monoid+import Data.Monoid (Monoid)+#endif --- Internal-import Data.MonadicStreamFunction+-- Internal imports+import Data.MonadicStreamFunction (MSF, morphGS) -- * 'Writer' 'MSF' running and wrapping
src/Data/MonadicStreamFunction.hs view
@@ -1,41 +1,45 @@--- | Monadic Stream Functions are synchronized stream functions--- with side effects.+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- 'MSF's are defined by a function--- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@--- that executes one step of a simulation, and produces an output in a--- monadic context, and a continuation to be used for future steps.+-- Monadic Stream Functions are synchronized stream functions with side+-- effects. ----- See the module "Data.MonadicStreamFunction.Core" for details.+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- that executes one step of a simulation, and produces an output in a monadic+-- context, and a continuation to be used for future steps. ----- 'MSF's are a generalisation of the implementation mechanism used by Yampa,--- Wormholes and other FRP and reactive implementations.+-- See the module "Data.MonadicStreamFunction.Core" for details. ----- When combined with different monads, they produce interesting effects. For--- example, when combined with the 'Maybe' monad, they become transformations--- that may stop producing outputs (and continuations). The 'Either' monad--- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and--- Monadic FRP).+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa,+-- Wormholes and other FRP and reactive implementations. ----- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@--- for a specific transformer @t@ often gives rise to known FRP constructs.--- For instance, flattening with 'EitherT' gives rise to switching, and--- flattening with 'ListT' gives rise to parallelism with broadcasting.+-- When combined with different monads, they produce interesting effects. For+-- example, when combined with the 'Maybe' monad, they become transformations+-- that may stop producing outputs (and continuations). The 'Either' monad+-- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and+-- Monadic FRP). ----- 'MSF's can be used to implement many FRP variants, including Arrowized FRP,--- Classic FRP, and plain reactive programming. Arrowized and applicative--- syntax are both supported.+-- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a+-- b@ for a specific transformer @t@ often gives rise to known FRP constructs.+-- For instance, flattening with 'EitherT' gives rise to switching, and+-- flattening with 'ListT' gives rise to parallelism with broadcasting. ----- For a very detailed introduction to 'MSF's, see:--- <http://dl.acm.org/citation.cfm?id=2976010>--- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).+-- 'MSF's can be used to implement many FRP variants, including Arrowized FRP,+-- Classic FRP, and plain reactive programming. Arrowized and applicative+-- syntax are both supported. ----- Apart from the modules exported, this module exports instances from:+-- For a very detailed introduction to 'MSF's, see:+-- <http://dl.acm.org/citation.cfm?id=2976010>+-- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>). ----- - "Data.MonadicStreamFunction.Instances.ArrowChoice"--- - "Data.MonadicStreamFunction.Instances.ArrowLoop"--- - "Data.MonadicStreamFunction.Instances.ArrowPlus"-+-- Apart from the modules exported, this module exports instances from:+--+-- - "Data.MonadicStreamFunction.Instances.ArrowChoice"+-- - "Data.MonadicStreamFunction.Instances.ArrowLoop"+-- - "Data.MonadicStreamFunction.Instances.ArrowPlus" module Data.MonadicStreamFunction ( module Control.Arrow , module Data.MonadicStreamFunction.Core@@ -43,17 +47,14 @@ ) where --- External-+-- External imports import Control.Arrow --- Internal-+-- Internal imports import Data.MonadicStreamFunction.Core import Data.MonadicStreamFunction.Util --- Internal (Instances)-+-- Internal imports (instances) import Data.MonadicStreamFunction.Instances.ArrowChoice () import Data.MonadicStreamFunction.Instances.ArrowLoop () import Data.MonadicStreamFunction.Instances.ArrowPlus ()
src/Data/MonadicStreamFunction/Async.hs view
@@ -1,59 +1,61 @@--- | This module contains operations on monadic streams that are asynchronous,--- i.e. that change the speed at which data enters or leaves the 'MSF'.-+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- This module contains operations on monadic streams that are asynchronous,+-- i.e. that change the speed at which data enters or leaves the 'MSF'. module Data.MonadicStreamFunction.Async where --- Internal-import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.InternalCore-import Data.MonadicStreamFunction.Util (MStream)--{- |-Concatenates a monadic stream of lists to a monadic stream.-The stream of lists will be called exactly when new data is needed.--Example:-->>> let intstream = constS $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int]->>> reactimate $ concatS intstream >>> arrM print-Enter a list of Ints:-[1,2,33]-1-2-33-Enter a list of Ints:-[]-Enter a list of Ints:-[]-Enter a list of Ints:-[1,2]-1-2-Enter a list of Ints:-...--Beware that @concatS msf@ becomes unproductive when @msf@ starts outputting empty lists forever.-This is ok:-->>> let boolToList b = if b then ["Yes"] else []->>> let everyOddEmpty = count >>> arr (even >>> boolToList)->>> reactimate $ concatS everyOddEmpty >>> arrM print-"Yes"-"Yes"-"Yes"-"Yes"-"Yes"-...--But this will be caught in a loop:+-- Internal imports+import Data.MonadicStreamFunction.InternalCore (MSF(MSF, unMSF))+import Data.MonadicStreamFunction.Util (MStream) ->>> let after3Empty = count >>> arr ((<= 3) >>> boolToList)->>> reactimate $ concatS after3Empty >>> arrM print-"Yes"-"Yes"-"Yes"-^CInterrupted.--}+-- |+-- Concatenates a monadic stream of lists to a monadic stream.+-- The stream of lists will be called exactly when new data is needed.+--+-- Example:+--+-- >>> let intstream = constS $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int]+-- >>> reactimate $ concatS intstream >>> arrM print+-- Enter a list of Ints:+-- [1, 2, 33]+-- 1+-- 2+-- 33+-- Enter a list of Ints:+-- []+-- Enter a list of Ints:+-- []+-- Enter a list of Ints:+-- [1, 2]+-- 1+-- 2+-- Enter a list of Ints:+-- ...+--+-- Beware that @concatS msf@ becomes unproductive when @msf@ starts outputting+-- empty lists forever. This is ok:+--+-- >>> let boolToList b = if b then ["Yes"] else []+-- >>> let everyOddEmpty = count >>> arr (even >>> boolToList)+-- >>> reactimate $ concatS everyOddEmpty >>> arrM print+-- "Yes"+-- "Yes"+-- "Yes"+-- "Yes"+-- "Yes"+-- ...+--+-- But this will be caught in a loop:+--+-- >>> let after3Empty = count >>> arr ((<= 3) >>> boolToList)+-- >>> reactimate $ concatS after3Empty >>> arrM print+-- "Yes"+-- "Yes"+-- "Yes"+-- ^CInterrupted. concatS :: Monad m => MStream m [b] -> MStream m b concatS msf = MSF $ \_ -> tick msf [] where@@ -61,6 +63,3 @@ tick msf' [] = do (bs, msf'') <- unMSF msf' () tick msf'' bs--- TODO Maybe this can be written more nicely with exceptions?--- Similarly takeS :: Int -> MSF m a b -> MSFExcept m a b () throws an exception after n ticks--- Or with merge?
src/Data/MonadicStreamFunction/Core.hs view
@@ -1,33 +1,44 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE Rank2Types #-}--- | Monadic Stream Functions are synchronized stream functions--- with side effects.+-- We disable the following warning because this module purposefully defines+-- orphan instances. This is a design decision in Dunai, so that we give+-- implementors further flexibility while giving most users the features they+-- expect.+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- 'MSF's are defined by a function--- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@--- that executes one step of a simulation, and produces an output in a--- monadic context, and a continuation to be used for future steps.+-- Monadic Stream Functions are synchronized stream functions with side+-- effects. ----- 'MSF's are a generalisation of the implementation mechanism used by Yampa,--- Wormholes and other FRP and reactive implementations.+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- that executes one step of a simulation, and produces an output in a monadic+-- context, and a continuation to be used for future steps. ----- When combined with different monads, they produce interesting effects. For--- example, when combined with the 'Maybe' monad, they become transformations--- that may stop producing outputs (and continuations). The 'Either' monad--- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and--- Monadic FRP).+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa,+-- Wormholes and other FRP and reactive implementations. ----- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@--- for a specific transformer @t@ often gives rise to known FRP constructs.--- For instance, flattening with 'EitherT' gives rise to switching, and--- flattening with 'ListT' gives rise to parallelism with broadcasting.+-- When combined with different monads, they produce interesting effects. For+-- example, when combined with the 'Maybe' monad, they become transformations+-- that may stop producing outputs (and continuations). The 'Either' monad+-- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and+-- Monadic FRP). ----- 'MSF's can be used to implement many FRP variants, including Arrowized FRP,--- Classic FRP, and plain reactive programming. Arrowized and applicative--- syntax are both supported.+-- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@+-- for a specific transformer @t@ often gives rise to known FRP constructs.+-- For instance, flattening with 'EitherT' gives rise to switching, and+-- flattening with 'ListT' gives rise to parallelism with broadcasting. ----- For a very detailed introduction to 'MSF's, see:--- <http://dl.acm.org/citation.cfm?id=2976010>--- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).+-- 'MSF's can be used to implement many FRP variants, including Arrowized FRP,+-- Classic FRP, and plain reactive programming. Arrowized and applicative+-- syntax are both supported.+--+-- For a very detailed introduction to 'MSF's, see:+-- <http://dl.acm.org/citation.cfm?id=2976010>+-- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>). module Data.MonadicStreamFunction.Core ( -- * Types MSF@@ -51,51 +62,53 @@ -- * Simulation , reactimate , embed- , module Control.Arrow+ , module X ) where -import Control.Applicative-import Control.Arrow-import Control.Category as C-import Control.Monad.Base-import Control.Monad.Trans.Class-import Data.Tuple (swap)-import Prelude hiding ((.), id, sum)+-- External imports+import Control.Arrow (Arrow (..), (>>>))+import qualified Control.Arrow as X+import Control.Category as C (id, (.))+import Control.Monad.Base (MonadBase, liftBase)+import Control.Monad.Trans.Class (MonadTrans, lift)+import Prelude hiding (id, sum, (.)) -import Data.MonadicStreamFunction.InternalCore (MSF, morphGS, feedback, reactimate, embed)+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative (Applicative(..))+#endif +-- Internal imports+import Data.MonadicStreamFunction.InternalCore (MSF, embed, feedback, morphGS,+ reactimate)+ -- * Definitions -- | 'Arrow' instance for 'MSF's. instance Monad m => Arrow (MSF m) where- arr f = arrM (return . f) - -- first sf = MSF $ \(a,c) -> do- -- (b, sf') <- unMSF sf a- -- b `seq` return ((b, c), first sf')-- first = morphGS $ \f (a,c) -> do- (b, msf') <- f a- return ((b, c), msf')-+ first =+ -- This implementation is equivalent to:+ -- first sf = MSF $ \(a, c) -> do+ -- (b, sf') <- unMSF sf a+ -- b `seq` return ((b, c), first sf')+ morphGS $ \f (a, c) -> do+ (b, msf') <- f a+ return ((b, c), msf') -- * Functor and applicative instances -- | 'Functor' instance for 'MSF's. instance Monad m => Functor (MSF m a) where fmap f msf = msf >>> arr f- -- fmap f msf = MSF $ fmap fS . unMSF msf- -- where- -- fS (b, cont) = (f b, fmap f cont) -- | 'Applicative' instance for 'MSF's. instance (Functor m, Monad m) => Applicative (MSF m a) where -- It is possible to define this instance with only Applicative m pure = arr . const- fs <*> bs = (fs &&& bs) >>> arr (uncurry ($)) + fs <*> bs = (fs &&& bs) >>> arr (uncurry ($)) -- ** Lifting point-wise computations @@ -107,11 +120,14 @@ -- -- Generalisation of 'arr' from 'Arrow' to monadic functions. arrM :: Monad m => (a -> m b) -> MSF m a b---arrM f = go--- where go = MSF $ \a -> do--- b <- f a--- return (b, go)-arrM f = morphGS (\i a -> i a >>= \(_,c) -> f a >>= \b -> return (b, c)) C.id+arrM f =+ -- This implementation is equivalent to:+ -- arrM f = go+ -- where+ -- go = MSF $ \a -> do+ -- b <- f a+ -- return (b, go)+ morphGS (\i a -> i a >>= \(_, c) -> f a >>= \b -> return (b, c)) C.id -- | Monadic lifting from one monad into another liftBaseM :: (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b@@ -125,6 +141,7 @@ liftBaseS = morphS liftBase -- *** MonadBase+ -- | Lift the first 'MSF' into the monad of the second. (^>>>) :: MonadBase m1 m2 => MSF m1 a b -> MSF m2 b c -> MSF m2 a c sf1 ^>>> sf2 = liftBaseS sf1 >>> sf2@@ -138,7 +155,6 @@ -- *** MonadTrans -- | Lift inner monadic actions in monad stacks.- liftTransS :: (MonadTrans t, Monad m, Monad (t m)) => MSF m a b -> MSF (t m) a b@@ -151,9 +167,9 @@ -- This is just a convenience function when you have a function to move across -- monads, because the signature of 'morphGS' is a bit complex. morphS :: (Monad m2, Monad m1)- => (forall c . m1 c -> m2 c)- -> MSF m1 a b- -> MSF m2 a b+ => (forall c . m1 c -> m2 c)+ -> MSF m1 a b+ -> MSF m2 a b morphS morph = morphGS morph' where -- The following makes the a's and the b's the same, and it just says:@@ -163,27 +179,11 @@ -- Remember that: -- morphGS :: Monad m2 -- => (forall c . (a1 -> m1 (b1, c)) -> (a2 -> m2 (b2, c)))- -- -- ^ The natural transformation. @mi@, @ai@ and @bi@ for @i = 1, 2@- -- -- can be chosen freely, but @c@ must be universally quantified+ -- -- ^ The natural transformation. @mi@, @ai@ and @bi@ for+ -- -- @i = 1, 2@ can be chosen freely, but @c@ must be+ -- -- universally quantified -- -> MSF m1 a1 b1 -- -> MSF m2 a2 b2 -- -- morph' :: (forall c . (a -> m1 (b, c)) -> (a -> m2 (b, c)))- morph' m1F = morph . m1F---- IPerez: There is an alternative signature for liftMStreamPurer that also--- works, and makes the code simpler:------ morphS :: Monad m => (m1 (b, MSF m1 a b) -> m (b, MSF m1 a b)) -> MSF m1 a b -> MSF m a b------ Then we can express:------ liftTransS = morphS lift--- liftBaseS = morphS liftBase------ We could also define a strict version of morphS as follows:------ morphS' f = morphS (f >=> whnfVal)--- where whnfVal p@(b,_) = b `seq` return p------ and leave morphS as a lazy version (by default).+ morph' m1F = morph . m1F
src/Data/MonadicStreamFunction/Instances/ArrowChoice.hs view
@@ -1,14 +1,21 @@-{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE InstanceSigs #-} {-# OPTIONS_GHC -fno-warn-orphans #-}--- | Instance of 'ArrowChoice' for Monadic Stream Functions ('MSF').+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Import this module to include that (orphan) instance.+-- Instance of 'ArrowChoice' for Monadic Stream Functions ('MSF').+--+-- Import this module to include that (orphan) instance. module Data.MonadicStreamFunction.Instances.ArrowChoice where -import Control.Arrow+-- External imports+import Control.Arrow (ArrowChoice (..)) -import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.InternalCore+-- Internal imports+import Data.MonadicStreamFunction.Core ()+import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) -- | 'ArrowChoice' instance for MSFs. instance Monad m => ArrowChoice (MSF m) where
src/Data/MonadicStreamFunction/Instances/ArrowLoop.hs view
@@ -1,24 +1,30 @@-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE RecursiveDo #-} {-# OPTIONS_GHC -fno-warn-orphans #-}--- | Instance of 'ArrowLoop' for Monadic Stream Functions ('MSF').+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Import this module to include that (orphan) instance.+-- Instance of 'ArrowLoop' for Monadic Stream Functions ('MSF'). ----- This is only defined for monads that are instances of 'MonadFix'.+-- Import this module to include that (orphan) instance.+--+-- This is only defined for monads that are instances of 'MonadFix'. module Data.MonadicStreamFunction.Instances.ArrowLoop where -import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.InternalCore+-- External imports+import Control.Arrow (ArrowLoop (..))+import Control.Monad.Fix (MonadFix) --- External-import Control.Arrow-import Control.Monad.Fix+-- Internal imports+import Data.MonadicStreamFunction.Core ()+import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) -- | 'ArrowLoop' instance for MSFs. The monad must be an instance of -- 'MonadFix'. instance MonadFix m => ArrowLoop (MSF m) where loop :: MSF m (b, d) (c, d) -> MSF m b c loop sf = MSF $ \a -> do- rec ((b,c), sf') <- unMSF sf (a, c)+ rec ((b, c), sf') <- unMSF sf (a, c) return (b, loop sf')
src/Data/MonadicStreamFunction/Instances/ArrowPlus.hs view
@@ -1,30 +1,37 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}--- | Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF').+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Import this module to include that (orphan) instance.+-- Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF'). ----- This is only defined for monads that are instances of 'MonadPlus'.+-- Import this module to include that (orphan) instance.+--+-- This is only defined for monads that are instances of 'MonadPlus'. module Data.MonadicStreamFunction.Instances.ArrowPlus where --- base-import Control.Arrow-import Control.Monad-import Control.Applicative+-- External imports+import Control.Applicative (Alternative (..))+import Control.Arrow (ArrowPlus (..), ArrowZero (..))+import Control.Monad (MonadPlus, mplus, mzero) --- dunai-import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.InternalCore+-- Internal imports+import Data.MonadicStreamFunction.Core ()+import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) --- | Instance of 'ArrowZero' for Monadic Stream Functions ('MSF').--- The monad must be an instance of 'MonadPlus'.+-- | Instance of 'ArrowZero' for Monadic Stream Functions ('MSF'). The monad+-- must be an instance of 'MonadPlus'. instance (Monad m, MonadPlus m) => ArrowZero (MSF m) where zeroArrow = MSF $ const mzero --- | Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF').--- The monad must be an instance of 'MonadPlus'.+-- | Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF'). The monad+-- must be an instance of 'MonadPlus'. instance (Monad m, MonadPlus m) => ArrowPlus (MSF m) where sf1 <+> sf2 = MSF $ \a -> unMSF sf1 a `mplus` unMSF sf2 a +-- | Instance of 'Alternative' for Monadic Stream Functions ('MSF'),+-- implemented using the 'ArrowZero' and 'ArrowPlus' instances. instance (Functor m, Monad m, MonadPlus m) => Alternative (MSF m a) where empty = zeroArrow (<|>) = (<+>)
src/Data/MonadicStreamFunction/Instances/Num.hs view
@@ -1,10 +1,13 @@-{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-}---- | Number instances for 'MSF's that produce numbers. This allows you to use--- numeric operators with 'MSF's that output numbers, for example,--- you can write:+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- Number instances for 'MSF's that produce numbers. This allows you to use+-- numeric operators with 'MSF's that output numbers, for example, you can+-- write:+-- -- @ -- msf1 :: MSF Input Double -- defined however you want -- msf2 :: MSF Input Double -- defined however you want@@ -18,13 +21,13 @@ -- msf3 = (msf1 &&& msf2) >>> arr (uncurry (+)) -- @ ----- Instances are provided for the type classes 'Num', 'Fractional'--- and 'Floating'.-+-- Instances are provided for the type classes 'Num', 'Fractional' and+-- 'Floating'. module Data.MonadicStreamFunction.Instances.Num where -import Control.Arrow.Util-import Data.MonadicStreamFunction.Core+-- Internal imports+import Control.Arrow.Util (constantly, elementwise, elementwise2)+import Data.MonadicStreamFunction.Core (MSF) -- | 'Num' instance for 'MSF's. instance (Monad m, Num b) => Num (MSF m a b) where
src/Data/MonadicStreamFunction/Instances/VectorSpace.hs view
@@ -1,12 +1,17 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | 'VectorSpace' instances for 'MSF's that produce vector spaces. This allows--- you to use vector operators with 'MSF's that output vectors, for example, you--- can write:+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk --+-- 'VectorSpace' instances for 'MSF's that produce vector spaces. This allows+-- you to use vector operators with 'MSF's that output vectors, for example,+-- you can write:+-- -- @ -- msf1 :: MSF Input (Double, Double) -- defined however you want -- msf2 :: MSF Input (Double, Double) -- defined however you want@@ -21,7 +26,7 @@ -- @ -- ----- Instances are provided for the type classes 'RModule' and 'VectorSpace'.+-- Instances are provided for the type class 'VectorSpace'. -- Note: This module uses undecidable instances, because GHC does not know -- enough to assert that it will be able to determine the type of 's' from the@@ -30,16 +35,27 @@ -- resolved. module Data.MonadicStreamFunction.Instances.VectorSpace where -import Control.Arrow-import Control.Arrow.Util-import Data.MonadicStreamFunction.Core-import Data.VectorSpace+-- External imports+import Control.Arrow ((>>^))+import Data.VectorSpace (VectorSpace (..)) +-- Internal imports+import Control.Arrow.Util (constantly, elementwise2)+import Data.MonadicStreamFunction.Core (MSF)+ -- | Vector-space instance for 'MSF's.-instance (Monad m, VectorSpace v s) => VectorSpace (MSF m a v) s where- zeroVector = constantly zeroVector- r *^ msf = msf >>^ (r *^)- msf ^/ r = msf >>^ (^/ r)- (^+^) = elementwise2 (^+^)- (^-^) = elementwise2 (^-^)- negateVector = (>>^ negateVector)+instance (Monad m, Eq s, Num s, VectorSpace v s, Floating (MSF m a s))+ => VectorSpace (MSF m a v) (MSF m a s)+ where+ zeroVector = constantly zeroVector+ (*^) = elementwise2 (*^)+ (^/) = elementwise2 (^/)+ (^+^) = elementwise2 (^+^)+ (^-^) = elementwise2 (^-^)+ negateVector = (>>^ negateVector)+ dot = elementwise2 dot+ normalize v = elementwise2 f v (norm v)+ where+ f v' nv'+ | nv' /= 0 = v' ^/ nv'+ | otherwise = error "normalize: zero vector"
src/Data/MonadicStreamFunction/InternalCore.hs view
@@ -1,52 +1,52 @@-{-# LANGUAGE ExplicitForAll #-}-{-# LANGUAGE Rank2Types #-}--- | Monadic Stream Functions are synchronized stream functions--- with side effects.+{-# LANGUAGE Rank2Types #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- 'MSF's are defined by a function--- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@--- that executes one step of a simulation, and produces an output in a--- monadic context, and a continuation to be used for future steps.+-- Monadic Stream Functions are synchronized stream functions with side+-- effects. ----- 'MSF's are a generalisation of the implementation mechanism used by Yampa,--- Wormholes and other FRP and reactive implementations.+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- that executes one step of a simulation, and produces an output in a monadic+-- context, and a continuation to be used for future steps. ----- This modules defines only the minimal core. Hopefully, other functions can be--- defined in terms of the functions in this module without accessing the--- MSF constuctor.-+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa,+-- Wormholes and other FRP and reactive implementations.+--+-- This modules defines only the minimal core. By default, you should import+-- "Data.MonadicStreamFunction.Core" or "Data.MonadicStreamFunction" whenever+-- possible, and define your functions without accessing the MSF constructor.+-- Those modules, as well as other modules in dunai, also provide convenient+-- instances. This module may be useful if you are extending dunai with+-- functionality that cannot be (conveniently) expressed using the existing+-- high-level API. -- NOTE TO IMPLEMENTORS: ----- This module contains the core. Only the core. It should be possible--- to define every function and type outside this module, except for the--- instances for ArrowLoop, ArrowChoice, etc., without access to the--- internal constructor for MSF and the function 'unMSF'.+-- This module contains the core. Only the core. It should be possible to+-- define every function and type outside this module, except for the instances+-- for ArrowLoop, ArrowChoice, etc., without access to the internal constructor+-- for MSF and the function 'unMSF'. ----- It's very hard to know what IS essential to framework and if we start--- adding all the functions and instances that *may* be useful in one--- module.+-- It's very hard to know what IS essential to framework and if we start adding+-- all the functions and instances that *may* be useful in one module. ----- By separating some instances and functions in other modules, we can--- easily understand what is the essential idea and then analyse how it--- is affected by an extension. It also helps demonstrate that something--- works for MSFs + ArrowChoice, or MSFs + ArrowLoop, etc.+-- By separating some instances and functions in other modules, we can easily+-- understand what is the essential idea and then analyse how it is affected by+-- an extension. It also helps demonstrate that something works for MSFs ++-- ArrowChoice, or MSFs + ArrowLoop, etc. ----- To address potential violations of basic design principles (like 'not--- having orphan instances'), the main module Data.MonadicStreamFunction--- exports everything. Users should *never* import this module here--- individually, but the main module instead.-+-- To address potential violations of basic design principles (like 'not having+-- orphan instances'), the main module Data.MonadicStreamFunction exports+-- everything. Users should *never* import this module here individually, but+-- the main module instead. module Data.MonadicStreamFunction.InternalCore where --- External-import Control.Arrow-import Control.Applicative-import Control.Category (Category(..))-import Control.Monad-import Control.Monad.Base-import Control.Monad.Trans.Class-import Prelude hiding ((.), id, sum)+-- External imports+import Control.Category (Category (..))+import Prelude hiding (id, sum, (.)) -- * Definitions @@ -62,7 +62,9 @@ -- | Instance definition for 'Category'. Defines 'id' and '.'. instance Monad m => Category (MSF m) where id = go- where go = MSF $ \a -> return (a, go)+ where+ go = MSF $ \a -> return (a, go)+ sf2 . sf1 = MSF $ \a -> do (b, sf1') <- unMSF sf1 a (c, sf2') <- unMSF sf2 b@@ -77,17 +79,15 @@ -- -- __Mathematical background:__ The type @a -> m (b, c)@ is a functor in @c@, -- and @MSF m a b@ is its greatest fixpoint, i.e. it is isomorphic to the type--- @a -> m (b, MSF m a b)@, by definition.--- The types @m@, @a@ and @b@ are parameters of the functor.--- Taking a fixpoint is functorial itself, meaning that a morphism--- (a natural transformation) of two such functors gives a morphism--- (an ordinary function) of their fixpoints.+-- @a -> m (b, MSF m a b)@, by definition. The types @m@, @a@ and @b@ are+-- parameters of the functor. Taking a fixpoint is functorial itself, meaning+-- that a morphism (a natural transformation) of two such functors gives a+-- morphism (an ordinary function) of their fixpoints. ----- This is in a sense the most general "abstract" lifting function,--- i.e. the most general one that only changes input, output and side effect--- types, and doesn't influence control flow.--- Other handling functions like exception handling or 'ListT' broadcasting--- necessarily change control flow.+-- This is in a sense the most general "abstract" lifting function, i.e. the+-- most general one that only changes input, output and side effect types, and+-- doesn't influence control flow. Other handling functions like exception+-- handling or 'ListT' broadcasting necessarily change control flow. morphGS :: Monad m2 => (forall c . (a1 -> m1 (b1, c)) -> (a2 -> m2 (b2, c))) -- ^ The natural transformation. @mi@, @ai@ and @bi@ for @i = 1, 2@@@ -110,14 +110,14 @@ -- | Apply a monadic stream function to a list. ----- Because the result is in a monad, it may be necessary to--- traverse the whole list to evaluate the value in the results to WHNF.--- For example, if the monad is the maybe monad, this may not produce anything--- if the 'MSF' produces 'Nothing' at any point, so the output stream cannot--- consumed progressively.+-- Because the result is in a monad, it may be necessary to traverse the whole+-- list to evaluate the value in the results to WHNF. For example, if the+-- monad is the maybe monad, this may not produce anything if the 'MSF'+-- produces 'Nothing' at any point, so the output stream cannot consumed+-- progressively. ----- To explore the output progressively, use 'arrM' and '(>>>)'', together--- with some action that consumes/actuates on the output.+-- To explore the output progressively, use 'arrM' and '(>>>)'', together with+-- some action that consumes/actuates on the output. -- -- This is called 'runSF' in Liu, Cheng, Hudak, "Causal Commutative Arrows and -- Their Optimization"
src/Data/MonadicStreamFunction/Parallel.hs view
@@ -1,18 +1,22 @@--- | Versions of arrow combinators that run things in parallel using 'par', if+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- Versions of arrow combinators that run things in parallel using 'par', if -- possible. module Data.MonadicStreamFunction.Parallel where --- External-import Control.Arrow-import GHC.Conc---- Internal-import Data.MonadicStreamFunction-import Data.MonadicStreamFunction.InternalCore+-- External imports+import Control.Arrow (arr, (>>>))+import GHC.Conc (par, pseq) --- | Run two 'MSF's in parallel, taking advantage of parallelism if--- possible. This is the parallel version of '***'.+-- Internal imports+import Data.MonadicStreamFunction ()+import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF)) +-- | Run two 'MSF's in parallel, taking advantage of parallelism if possible.+-- This is the parallel version of '***'. (*|*) :: Monad m => MSF m a b -> MSF m c d -> MSF m (a, c) (b, d) msf1 *|* msf2 = MSF $ \(a, c) -> do (b, msf1') <- unMSF msf1 a
src/Data/MonadicStreamFunction/ReactHandle.hs view
@@ -1,31 +1,31 @@--- | 'ReactHandle's.+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk ----- Sometimes it is beneficial to give control to an external main loop,--- for example OpenGL or a hardware-clocked audio server like JACK.--- This module makes Dunai compatible with external main loops.-+-- 'ReactHandle's.+--+-- Sometimes it is beneficial to give control to an external main loop, for+-- example OpenGL or a hardware-clocked audio server like JACK. This module+-- makes Dunai compatible with external main loops. module Data.MonadicStreamFunction.ReactHandle where --- External-import Control.Monad.IO.Class-import Data.IORef---- Internal-import Data.MonadicStreamFunction-import Data.MonadicStreamFunction.InternalCore+-- External imports+import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.IORef (IORef, newIORef, readIORef, writeIORef) +-- Internal imports+import Data.MonadicStreamFunction (MSF)+import Data.MonadicStreamFunction.InternalCore (unMSF) --- | A storage for the current state of an 'MSF'.--- The 'MSF' may not require input or produce output data,--- all such data must be handled through side effects--- (such as wormholes).+-- | A storage for the current state of an 'MSF'. The 'MSF' may not require+-- input or produce output data, all such data must be handled through side+-- effects (such as wormholes). type ReactHandle m = IORef (MSF m () ()) - -- | Needs to be called before the external main loop is dispatched. reactInit :: MonadIO m => MSF m () () -> m (ReactHandle m) reactInit = liftIO . newIORef- -- | The callback that needs to be called by the external loop at every cycle. react :: MonadIO m => ReactHandle m -> m ()
src/Data/MonadicStreamFunction/Util.hs view
@@ -1,40 +1,40 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE Rank2Types #-}--- | Useful auxiliary functions and definitions.+{-# LANGUAGE Arrows #-}+{-# LANGUAGE CPP #-}+-- |+-- Copyright : (c) Ivan Perez and Manuel Baerenz, 2016+-- License : BSD3+-- Maintainer : ivan.perez@keera.co.uk+--+-- Useful auxiliary functions and definitions. module Data.MonadicStreamFunction.Util where --- External-import Control.Arrow-import Control.Category-import Control.Monad-import Control.Monad.Base-import Data.Monoid+-- External imports+import Control.Arrow (arr, returnA, (&&&), (<<<), (>>>))+import Control.Category (id, (.))+import Control.Monad (when)+import Data.VectorSpace (VectorSpace, zeroVector, (^+^))+import Prelude hiding (id, (.)) --- Internal-import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.Instances.ArrowChoice ()-import Data.VectorSpace-import Prelude hiding (id, (.))+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (Monoid, mempty, mappend)+#endif -import Control.Monad.Trans.MSF.State+-- Internal imports+import Data.MonadicStreamFunction.Core (MSF, arrM, feedback)+import Data.MonadicStreamFunction.Instances.ArrowChoice () -- * Streams and sinks --- | A stream is an 'MSF' that produces outputs, while ignoring the input.--- It can obtain the values from a monadic context.+-- | A stream is an 'MSF' that produces outputs, while ignoring the input. It+-- can obtain the values from a monadic context. type MStream m a = MSF m () a --- | A sink is an 'MSF' that consumes inputs, while producing no output.--- It can consume the values with side effects.-type MSink m a = MSF m a ()---- * Lifting--+-- | A sink is an 'MSF' that consumes inputs, while producing no output. It+-- can consume the values with side effects.+type MSink m a = MSF m a () -- * Analogues of 'map' and 'fmap' - -- | Apply an 'MSF' to every input. Freezes temporarily if the input is -- 'Nothing', and continues as soon as a 'Just' is received. mapMaybeS :: Monad m => MSF m a b -> MSF m (Maybe a) (Maybe b)@@ -55,17 +55,13 @@ -- * Delays --- See also: 'iPre'- -- | Delay a signal by one sample. iPre :: Monad m => a -- ^ First output -> MSF m a a--- iPre firsta = MSF $ \a -> return (firsta, iPre a) iPre firsta = feedback firsta $ arr swap- where swap (a,b) = (b, a)--- iPre firsta = next firsta identity-+ where+ swap (a, b) = (b, a) -- | Preprends a fixed output to an 'MSF'. The first input is completely -- ignored.@@ -78,15 +74,20 @@ next :: Monad m => b -> MSF m a b -> MSF m a b next b sf = sf >>> iPre b --- | Buffers and returns the elements in FIFO order,--- returning 'Nothing' whenever the buffer is empty.+-- | Buffers and returns the elements in FIFO order, returning 'Nothing'+-- whenever the buffer is empty. fifo :: Monad m => MSF m [a] (Maybe a)-fifo = feedback [] $ proc (as, accum) -> do- let accum' = accum ++ as- returnA -< case accum' of- [] -> (Nothing, [])- (a : as) -> (Just a , as)+fifo = feedback [] (arr (safeSnoc . uncurry fifoAppend))+ where+ -- | Append a new list to an accumulator in FIFO order.+ fifoAppend :: [x] -> [x] -> [x]+ fifoAppend as accum = accum ++ as + -- | Split a list into the head and the tail.+ safeSnoc :: [x] -> (Maybe x, [x])+ safeSnoc [] = (Nothing, [])+ safeSnoc (x:xs) = (Just x, xs)+ -- * Folding -- ** Folding for 'VectorSpace' instances@@ -116,16 +117,15 @@ -- ** Generic folding \/ accumulation --- | Applies a function to the input and an accumulator,--- outputting the updated accumulator.--- Equal to @\f s0 -> feedback s0 $ arr (uncurry f >>> dup)@.+-- | Applies a function to the input and an accumulator, outputting the updated+-- accumulator. Equal to @\f s0 -> feedback s0 $ arr (uncurry f >>> dup)@. accumulateWith :: Monad m => (a -> s -> s) -> s -> MSF m a s accumulateWith f s0 = feedback s0 $ arr g where g (a, s) = let s' = f a s in (s', s') --- | Applies a transfer function to the input and an accumulator,--- returning the updated accumulator and output.+-- | Applies a transfer function to the input and an accumulator, returning the+-- updated accumulator and output. mealy :: Monad m => (a -> s -> (b, s)) -> s -> MSF m a b mealy f s0 = feedback s0 $ arr $ uncurry f @@ -137,14 +137,13 @@ unfold f a = feedback a (arr (snd >>> f)) -- | Generate outputs using a step-wise generation function and an initial--- value. Version of 'unfold' in which the output and the new accumulator--- are the same. Should be equal to @\f a -> unfold (f >>> dup) a@.+-- value. Version of 'unfold' in which the output and the new accumulator are+-- the same. Should be equal to @\f a -> unfold (f >>> dup) a@. repeatedly :: Monad m => (a -> a) -> a -> MSF m () a repeatedly f = unfold $ f >>> dup where dup a = (a, a) - -- * Debugging -- | Outputs every input sample, with a given message prefix.@@ -159,7 +158,11 @@ -- | Outputs every input sample, with a given message prefix, using an -- auxiliary printing function, when a condition is met.-traceWhen :: (Monad m, Show a) => (a -> Bool) -> (String -> m ()) -> String -> MSF m a a+traceWhen :: (Monad m, Show a)+ => (a -> Bool)+ -> (String -> m ())+ -> String+ -> MSF m a a traceWhen cond method msg = withSideEffect $ \a -> when (cond a) $ method $ msg ++ show a