Showing preview only (241K chars total). Download the full file or copy to clipboard to get everything.
Repository: Gabriel439/Haskell-Pipes-Library
Branch: main
Commit: 3997b02a5e22
Files: 22
Total size: 232.3 KB
Directory structure:
gitextract_l6xj46in/
├── .github/
│ └── workflows/
│ └── haskell.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── Setup.hs
├── benchmarks/
│ ├── Common.hs
│ ├── LiftBench.hs
│ └── PreludeBench.hs
├── laws.md
├── nix/
│ └── .gitkeep
├── pipes.cabal
├── release.nix
├── shell.nix
├── src/
│ ├── Pipes/
│ │ ├── Core.hs
│ │ ├── Internal.hs
│ │ ├── Lift.hs
│ │ ├── Prelude.hs
│ │ └── Tutorial.hs
│ └── Pipes.hs
├── stack.yaml
└── tests/
└── Main.hs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/haskell.yml
================================================
name: Haskell CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: haskell/actions/setup@v1.2
- name: Cache
uses: actions/cache@v1
env:
cache-name: cache-cabal
with:
path: ~/.cabal
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install dependencies
run: |
cabal update
cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build
run: cabal build --enable-tests --enable-benchmarks all
- name: Run tests
run: cabal test all
================================================
FILE: .gitignore
================================================
cabal-dev
.cabal-sandbox
cabal.sandbox.config
dist
.stack-work
================================================
FILE: CHANGELOG.md
================================================
4.3.16
* Fix example code for `every`
* Improved documentation for `ListT`
4.3.15
* Build against `ghc-9.0`
4.3.14
* Add `mapMaybe` and `wither`, and more laws for `filter` and `filterM`.
4.3.13
* Add `MonadFail` instance for `Proxy`
4.3.12
* Fix space leak introduced in version 4.3.10
* This leak primarily affects the use of `forever`
4.3.11
* Fix documentation for `scanM`
4.3.10
* Relax `Monad` constraints to `Functor`
* Support GHC 8.8
4.3.9
* Increase upper bound on `exceptions`
4.3.8
* Increase upper bound on `exceptions`
4.3.7
* Documentation fix
4.3.6
* Fix implementation of `pass` in `MonadWriter` instance for `Proxy`
4.3.5
* Support `Semigroup` being a super-class of `Monoid`
4.3.4
* Increase upper bound on `mmorph`
4.3.3
* Make `X` a synonym for `Data.Void.Void`
4.3.2
* BUG FIX: Fix `MMonad` instance for `ListT`
* The old instance was an infinite loop
4.3.1
* Support building against `ghc-7.4`
4.3.0
* BREAKING CHANGE: Remove `Alternative`/`MonadPlus` instances for `Proxy`
* See commit 08e7302f43dbf2a40bd367c5ee73ee3367e17768 which explains why
* Add `Traversable` instance for `ListT`
* New `MonadThrow`/`MonadCatch`/`MMonad`/`Semigroup`/`MonadZip` instances for
`ListT`
* New `MonadThrow`/`MonadCatch` instances for `Proxy`
* Fix lower bound on `mtl`
* Increase upper bound on `optparse-applicative`
4.2.0
* BREAKING CHANGE: Switch from `ErrorT` to `ExceptT`
* Add `Foldable` instance for `ListT`
* Fix all warnings
* Enable foldr/build fusion for `toList`
4.1.9
* Increase lower bound on `criterion`
* Increase upper bound on `transformers` for tests/benchmarks
* Optimize code by delaying `INLINABLE` annotations
4.1.8
* Increase upper bound on `transformers`
* Prepare for MRP (Monad of no Return Proposal)
4.1.7
* Increase lower bound on `deepseq`
* Add `unfoldr`
* Add `loop`
* Add `toListM'`
* Improve efficiency of `drop`
* License tutorial under Creative Commons license
4.1.6
* Increase lower bound on `base`
* Add diagrams to `Pipes.Core` documentation
* Add `mapM_`
* Add `takeWhile'`
* Add `seq`
* Improve efficiency of `toListM`
4.1.5
* Increase upper bound on `criterion`
4.1.4
* Increase upper bound on `criterion`
* Add `Monoid` instance for `Proxy`
4.1.3
* Increase lower bound on `mtl`
* Re-export `void`
* Add `fold'`
* Add `foldM'`
4.1.2
* Increase upper bounds on `transformers` and `mtl`
4.1.1
* Add `runListT`
* Add `MMonad` instance for `Proxy`
* Add `repeatM`
* Add laws to documentation of `Pipes.Prelude` utilities
4.1.0
* Remove Haskell98 support
* Use internal `X` type instead of `Data.Void`
* Document `Pipes.Lift` module:w
* Add `drain`
* Add `sequence`
4.0.2
* Improve performance of `each`
* Add tutorial appendix explaining how to work around quadratic time complexity
4.0.1
* Remove `WriterT` and `RWST` benchmarks
* Add `Enumerable` instance for `ErrorT`
* Add cabal flag for Haskell98 compilation
* Add several rewrite rules
* Add `mtl` instances for `ListT`
* Fix implementation of `pass`, which did not satisfy `Writer` laws
* Implement `fail` for `ListT`
* Add type synonym table to tutorial appendix
* Add QuickCheck tests for `pipes` laws
* Add `mapFoldable`
* Add `Monoid` instance for `ListT`
* Add manual proofs of `pipes` laws in `laws.md`
4.0.0
Major upgrade of `pipes` to no longer use `Proxy` type class
================================================
FILE: LICENSE
================================================
Copyright (c) 2012-2016 Gabriella Gonzalez
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 Gabriella Gonzalez 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.
================================================
FILE: README.md
================================================
# `pipes`
`pipes` is a clean and powerful stream processing library that lets you build
and connect reusable streaming components.
## Quick start
* Install the [Haskell Platform](http://www.haskell.org/platform/)
* `cabal install pipes`
Then fire up `ghci`:
$ ghci
Prelude> import Pipes
Prelude Pipes> import qualified Pipes.Prelude as P
... and echo standard input to standard output until you enter `quit`.
Prelude Pipes P> runEffect $ P.stdinLn >-> P.takeWhile (/= "quit") >-> P.stdoutLn
Test[Enter]
Test
Apple[Enter]
Apple
quit[Enter]
Prelude P> -- Done!
To learn more, read the
[pipes tutorial](http://hackage.haskell.org/package/pipes/docs/Pipes-Tutorial.html).
## Features
* *Concise API*: Use simple commands like `for`, `(>->)`, `await`, and `yield`
* *Blazing fast*: Implementation tuned for speed, including shortcut fusion
* *Lightweight Dependency*: `pipes` is small and compiles very rapidly,
including dependencies
* *Elegant semantics*: Use practical category theory
* *ListT*: Correct implementation of `ListT` that interconverts with pipes
* *Bidirectionality*: Implement duplex channels
* *Extensive Documentation*: Second to none!
## Philosophy
The `pipes` library emphasizes the following three design precepts:
* Emphasize elegance - Elegant libraries replace inelegant libraries
* Theory everywhere - Principled design promotes intuitive behavior
* Minimize dependencies - Small dependency profiles maximize portability
## Outline
The core `pipes` ecosystem consists of the following four libraries:
* `pipes`: The elegant, theoretically-principled core.
* `pipes-concurrency`: Message passing and reactive programming
* `pipes-parse`: Utilities for parsing streams
* `pipes-safe`: Resource management and exception safety
These represent the core areas that I envision for `pipes`. The latter three
libraries represent the more pragmatic face of the ecosystem and make design
tradeoffs where there is no known elegant solution.
Derived libraries that build on top of these include:
* `pipes-network` and `pipes-network-tls`: Networking support
* `pipes-attoparsec`: High-efficiency streaming parsing
* `pipes-zlib`: Compression and decompression
* `pipes-binary`: Streaming serialization and deserialization
* `pipes-aeson`: Streaming JSON encoding and decoding
## Development Status
[](https://travis-ci.org/Gabriella439/Haskell-Pipes-Library)
`pipes` is stable, and current work focuses on packaging `pipes` for various
package managers. The long term goal is to get `pipes` into the Haskell
platform and become the basic building block for streaming APIs.
## Community Resources
* [Haskell wiki page](http://www.haskell.org/haskellwiki/Pipes)
* [Mailing list](mailto:haskell-pipes@googlegroups.com) ([Google Group](https://groups.google.com/forum/?fromgroups#!forum/haskell-pipes))
## How to contribute
* Contribute code
* Build derived libraries
* Write `pipes` tutorials
## License (BSD 3-clause)
Copyright (c) 2012-2014 Gabriella Gonzalez
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 Gabriella Gonzalez 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.
================================================
FILE: Setup.hs
================================================
import Distribution.Simple
main = defaultMain
================================================
FILE: benchmarks/Common.hs
================================================
module Common (commonMain) where
import Criterion.Main (Benchmark, runMode)
import Criterion.Main.Options as Criterion
import Data.Maybe (fromMaybe)
import Data.Monoid
import Options.Applicative
commonMain :: Int -- ^ default maximum data size
-> (Int -> [Benchmark]) -- ^ the benchmarks to run
-> IO ()
commonMain mdMax bench = do
(maybeNewMax, critMode) <- execParser $ info (helper <*> options) mempty
runMode critMode $ bench (fromMaybe mdMax maybeNewMax)
options :: Parser (Maybe Int, Criterion.Mode)
options =
(,) <$> optional (option auto (help "benchmark maximum data size"
<> metavar "N" <> short 'i' <> long "imax"))
<*> Criterion.parseWith Criterion.defaultConfig
================================================
FILE: benchmarks/LiftBench.hs
================================================
{-# LANGUAGE RankNTypes #-}
module Main (main) where
import Common (commonMain)
import Control.Monad.Identity
import qualified Control.Monad.Trans.Reader as R
import qualified Control.Monad.Trans.State.Strict as S
import Criterion.Main
import Data.Monoid
import Pipes
import Pipes.Lift
defaultMax :: Int
defaultMax = 10000
main :: IO ()
main = commonMain defaultMax liftBenchmarks
iter :: forall m a . (Monad m , Ord a, Num a) => (a -> m a) -> a -> Effect m a
iter a vmax = loop 0
where
loop n
| n > vmax = return vmax
| otherwise = do
x <- lift $ a n
loop $! x
s_bench :: Int -> Effect (S.StateT Int Identity) Int
s_bench = iter (\n -> S.get >>= (\a -> S.put $! a + n) >> return (n + 1))
r_bench :: Int -> Effect (R.ReaderT Int Identity) Int
r_bench = iter (\n -> R.ask >>= (\a -> return $ n + a))
-- Run before Proxy
runB :: (a -> Effect Identity r) -> a -> r
runB f a = runIdentity $ runEffect $ f a
-- Run after Proxy
runA :: (Monad m) => (m r -> Identity a) -> Effect m r -> a
runA f a = runIdentity $ f (runEffect a)
liftBenchmarks :: Int -> [Benchmark]
liftBenchmarks vmax =
let applyBench = map ($ vmax)
in
[
bgroup "ReaderT" $
let defT f = (\d -> f d 1)
in applyBench
[
bench "runReaderP_B" . whnf (runB (runReaderP 1) . r_bench)
, bench "runReaderP_A" . whnf (runA (defT R.runReaderT) . r_bench)
]
, bgroup "StateT" $
let defT f = (\s -> f s 0)
in applyBench
[
bench "runStateP_B" . nf (runB (runStateP 0) . s_bench)
, bench "runStateP_A" . nf (runA (defT S.runStateT) . s_bench)
, bench "evalStateP_B" . whnf (runB (evalStateP 0) . s_bench)
, bench "evalStateP_A" . whnf (runA (defT S.evalStateT) . s_bench)
, bench "execStateP_B" . whnf (runB (execStateP 0) . s_bench)
, bench "execStateP_A" . whnf (runA (defT S.execStateT) . s_bench)
]
]
================================================
FILE: benchmarks/PreludeBench.hs
================================================
{-# LANGUAGE RankNTypes #-}
module Main (main) where
import Criterion.Main
import Common (commonMain)
import Control.Monad.Identity (Identity, runIdentity)
import Pipes
import qualified Pipes.Prelude as P
import Prelude hiding (enumFromTo)
defaultMax :: Int
defaultMax = 10000
main :: IO ()
main = commonMain defaultMax preludeBenchmarks
enumFromTo :: (Int -> a) -> Int -> Int -> Producer a Identity ()
enumFromTo f n1 n2 = loop n1
where
loop n =
if n <= n2
then do
yield $! f n
loop $! n + 1
else return ()
{-# INLINABLE enumFromTo #-}
drain :: Producer b Identity r -> r
drain p = runIdentity $ runEffect $ for p discard
msum :: (Monad m) => Producer Int m () -> m Int
msum = P.foldM (\a b -> return $ a + b) (return 0) return
scanMSum :: (Monad m) => Pipe Int Int m r
scanMSum = P.scanM (\x y -> return (x + y)) (return 0) return
-- Using runIdentity seems to reduce outlier counts.
preludeBenchmarks :: Int -> [Benchmark]
preludeBenchmarks vmax =
let applyBench b = b benchEnum_p
benchEnum_p = enumFromTo id 1 vmax
in
[
bgroup "Folds" $ map applyBench
[
bench "all" . whnf (runIdentity . P.all (<= vmax))
, bench "any" . whnf (runIdentity . P.any (> vmax))
, bench "find" . whnf (runIdentity . P.find (== vmax))
, bench "findIndex" . whnf (runIdentity . P.findIndex (== vmax))
, bench "fold" . whnf (runIdentity . P.fold (+) 0 id)
, bench "foldM" . whnf (runIdentity . msum)
, bench "head" . nf (runIdentity . P.head)
, bench "index" . nf (runIdentity . P.index (vmax-1))
, bench "last" . nf (runIdentity . P.last)
, bench "length" . whnf (runIdentity . P.length)
, bench "null" . whnf (runIdentity . P.null)
, bench "toList" . nf P.toList
]
, bgroup "Pipes" $ map applyBench
[
bench "chain" . whnf (drain . (>-> P.chain (\_ -> return ())))
, bench "drop" . whnf (drain . (>-> P.drop vmax))
, bench "dropWhile" . whnf (drain . (>-> P.dropWhile (<= vmax)))
, bench "filter" . whnf (drain . (>-> P.filter even))
, bench "findIndices" . whnf (drain . (>-> P.findIndices (<= vmax)))
, bench "map" . whnf (drain . (>-> P.map id))
, bench "mapM" . whnf (drain . (>-> P.mapM return))
, bench "take" . whnf (drain . (>-> P.take vmax))
, bench "takeWhile" . whnf (drain . (>-> P.takeWhile (<= vmax)))
, bench "scan" . whnf (drain . (>-> P.scan (+) 0 id))
, bench "scanM" . whnf (drain . (>-> scanMSum))
] ++ [
bench "concat" $ whnf (drain . (>-> P.concat)) $ enumFromTo Just 1 vmax
]
, bgroup "Zips" $ map applyBench
[
bench "zip" . whnf (drain . P.zip benchEnum_p)
, bench "zipWith" . whnf (drain . P.zipWith (+) benchEnum_p)
]
, bgroup "enumFromTo.vs.each"
[
bench "enumFromTo" $ whnf (drain . enumFromTo id 1) vmax
, bench "each" $ whnf (drain . each) [1..vmax]
]
]
================================================
FILE: laws.md
================================================
# Kleisli Category
Define:
return
:: (Monad m)
=> r -> Proxy a' a b' b m r
return = Pure
(>=>)
:: (Monad m)
=> (r -> Proxy a' a b' b m s)
-> (s -> Proxy a' a b' b m t)
-> (r -> Proxy a' a b' b m t)
(f >=> g) x = f x >>= g
(>>=)
:: (Monad m)
=> Proxy a' a b' b m s
-> (s -> Proxy a' a b' b m t)
-> Proxy a' a b' b m t
p >>= f = case p of
Request a' fa -> Request a' (\a -> fa a >>= f)
Respond b fb' -> Respond b (\b' -> fb' b' >>= f)
M m -> M (m >>= \p' -> return (p' >>= f))
Pure r -> f r
## Left Identity Law
Goal: return >=> f = f
return >=> f
-- Definition of `(>=>)`
= \x -> return x >>= f
-- [Kleisli Category - Left Identity Law - Pointful]
= \x -> f x
-- Eta reduce
= f
-- Goal complete
### Pointful
Goal: return r >>= f = f r
return r >>= f
-- Definition of `return`
= Pure r >>= f
-- Definition of `(>>=)`
= f r
-- Goal complete
## Right Identity Law
Goal: f >=> return = f
f >=> return
-- Definition of `(>=>)`
= \x -> f x >>= return
-- [Kleisli Category - Right Identity Law - Pointful]
= \x -> f x
-- Eta reduce
= f
-- Goal complete
### Pointful
Goal: p >>= return = p
p >>= return
-- Definition of `(>>=)`
= case p of
Request a' fa -> Request a' (\a -> fa a >>= return)
-- Coinduction: Reuse the premise
= Request a' (\a -> fa a)
-- Eta reduce
= Request a' fa
Respond b fb' -> Respond b (\b' -> fb' b' >>= return)
-- Coinduction: Reuse the premise
= Respond b (\b' -> fb' b')
-- Eta reduce
= Respond b fb'
M m -> M (m >>= \p' -> return (p' >>= return))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p')
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful] (NOTE: for base monad, so no coinduction necessary)
= M m
Pure r -> return r
-- Definition of `return`
= Pure r
-- Clean up
= case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r
-- case statement = id
= p
-- Goal complete
## Associativity Law
Goal: (f >=> g) >=> h = f >=> (g >=> h)
(f >=> g) >=> h
-- Definition of `(>=>)`
= \x -> (f >=> g) x >>= h
-- Definition of `(>=>)`
= \x -> (f x >>= g) >>= h
-- [Kleisli Category - Associativity Law - Pointful]
= \x -> f x >>= \y -> g y >>= h
-- Definition of `(>=>)`, in reverse
= \x -> f x >>= (g >=> h)
-- Definition of `(>=>)`, in reverse
= f >=> (g >=> h)
-- Goal complete
### Pointful
Goal: (p >>= f) >>= g) = p >>= \x -> f x >>= g
(p >>= f) >>= g
-- Definition of `(>>=)`
(case p of
Request a' fa -> Request a' (\a -> fa a >>= f)
Respond b fb' -> Respond b (\b' -> fb' b' >>= f)
M m -> M (m >>= \p' -> return (p' >>= f))
Pure r -> f r ) >>= g
-- Distribute over case statement
= case p of
Request a' fa -> Request a' (\a -> fa a >>= f) >>= g
-- Definition of `(>>=)`
= Request a' (\a -> (fa a >>= f) >>= g)
-- Coinduction: Reuse the premise
= Request a' (\a -> fa a >>= \x -> f x >>= g)
-- Definition of `(>>=)`, in reverse
= Request a' fa >>= \x -> f x >>= g
Respond b fb' -> Respond b (\b' -> fb' b' >>= f) >>= g
-- Definition of `(>>=)`
= Respond b (\b' -> (fb' b' >>= f) >>= g)
-- Coinduction: Reuse the premise
= Respond b (\b' -> fb' b' >>= \x -> f x >>= g)
-- Definition of `(>>=)`, in reverse
= Respond b fb' >>= \x -> f x >>= g
M m -> M (m >>= \p' -> return (p' >>= f)) >>= g
-- Definition of `(>>=)`
= M (m >>= \p' -> return ((p' >>= f) >>= g))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (p' >>= \x -> f x >>= g))
-- Definition of `(>>=)`, in reverse
= M m >>= \x -> f x >>= g
Pure r -> f r >>= g
-- Definition of `(>>=)`, in reverse
= Pure r >>= \x -> f x >>= g
-- Clean up
= case p of
Request a' fa -> Request a' fa >>= \x -> f x >>= g
Respond b fb' -> Respond b fb' >>= \x -> f x >>= g
M m -> M m >>= \x -> f x >>= g
Pure r -> Pure r >>= \x -> f x >>= g
-- Factor from case statement
= (case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r ) >>= \x -> f x >>= g
-- case statement = id
= p >>= \x -> f x >>= g
-- Goal complete
# Respond Category
Define:
respond
:: (Monad m)
=> a -> Proxy x' x a' a m a'
respond a = Respond a Pure
(/>/)
:: (Monad m)
=> (a -> Proxy x' x b' b m a')
-> (b -> Proxy x' x c' c m b')
-> (a -> Proxy x' x c' c m a')
(fa />/ fb) a = fa a //> fb
(//>)
:: (Monad m)
=> Proxy x' x b' b m a'
-> (b -> Proxy x' x c' c m b')
-> Proxy x' x c' c m a'
p //> fb = case p of
Request x' fx -> Request x' (\x -> fx x //> fb)
Respond b fb' -> fb b >>= \b' -> fb' b' //> fb
M m -> M (m >>= \p' -> return (p' //> fb)
Pure a -> Pure a
## Right Identity Law
Goal: respond />/ fb = fb
respond />/ fb
-- Definition of `(/>/)`
= \b -> respond b //> fb
-- [Respond Category - Left Identity Law - Pointful]
= \b -> fb b
-- Eta reduce
= fb
-- Goal complete
### Pointful
Goal: respond b //> fb = fb b
-- Definition of `respond`
= Respond b Pure //> fb
-- Definition of `(//>)`
= fb b >>= \b' -> Pure b' //> fb
-- Definition of `(//>)`
= fb b >>= \b' -> Pure b'
-- Eta reduce
= fb b >>= Pure
-- Definition of `return` (in reverse)
= fb b >>= return
-- [Kleisli Category - Right Identity Law - Pointful]
= fb b
-- Goal complete
## Left Identity Law
Goal: fa />/ respond = fa
fa />/ respond
-- Definition of '(/>/)'
= \a -> fa a //> respond
-- [Respond Category - Left Identity Law - Pointful]
= \a -> fa a
-- Eta reduce
= fa
-- Goal complete
### Pointful
Goal: "Pointful": p //> respond = p
p //> respond
-- Definition of `(//>)`
= case p of
Request x' fx -> Request x' (\x -> fx x //> respond)
-- Coinduction: Reuse the premise
= Request x' (\x -> fx x)
-- Eta reduce
= Request x' fx
Respond b fb' -> respond b >>= \b' -> fb' b' //> respond
-- Coinduction: Reuse the premise
= respond b >>= \b' -> fb' b'
-- Eta reduce
= respond b >>= fb'
-- Definition of `respond`
= Respond b Pure >>= fb'
-- Definition of `(>>=)`
= Respond b (\b' -> Pure b' >>= fb')
-- Definition of `return`, backwards
= Respond b (\b' -> return b' >>= fb')
-- [Kleisli Category - Left Identity Law - Pointful]
= Respond b (\b' -> fb' b')
-- Eta reduce
= Respond b fb'
M m -> M (m >>= \p' -> return (p' //> respond))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p')
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful]
= M m
Pure a' -> Pure a'
-- Clean up
= case p of
Request x' fx -> Request x' fx
Respond b fb' -> Respond b fb'
M m -> M m
Pure a' -> Pure a'
-- case statement = id
= p
-- Goal complete
## Associativity Law
Goal: (fa />/ fb) />/ fc = fa />/ (fb />/ fc)
(fa />/ fb) />/ fc
-- Definition of `(/>/)`
\a -> ((fa />/ fb) a) //> fc
-- Definition of `(/>/)`
\a -> (fa a //> fb) //> fc
-- [Respond Category - Associativity Law - Pointful]
\a -> fa a //> \b -> fb b //> fc
-- Definition of `(/>/)`, in reverse
\a -> fa a //> (fb />/ fc)
-- Definition of `(/>/)`, in reverse
fa />/ (fb />/ fc)
-- Goal complete
### Pointful
Goal: (p //> fb) //> fc = p //> \b -> fb b //> fc
(p //> fb) //> fc
-- Definition of `(//>)`
= (case p of
Request x' fx -> Request x' (\x -> fx x //> fb)
Respond b fb' -> fb b >>= \b' -> fb' b' //> fb
M m -> M (m >>= \p' -> return (p' //> fb))
Pure a' -> Pure a' ) //> fc
-- Distribute over case statement
= case p of
Request x' fx -> Request x' (\x -> fx x //> fb) //> fc
-- Definition of `(//>)`
= Request x' (\x -> (fx x //> fb) //> fc)
-- Coinduction: Reuse the premise
= Request x' (\x -> fx x //> \b -> fb b //> fc)
-- Definition of `(//>)`, in reverse
= Request x' fx //> \b -> fb //> fc
Respond b fb' -> (fb b >>= \b' -> fb' b' //> fb) //> fc
-- [Respond Category - Distributivity Law - Pointful]
= (fb b //> fc) >>= \b' -> (fb' b' //> fb) //> fc
-- Coinduction: Reuse the premise
= (fb b //> fc) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- [Kleisli Category - Right Identity Law - Pointful], in reverse
= ((fb b //> fc) >>= return) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- Definition of `return`
= ((fb b //> fc) >>= Pure) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- Eta expand
= ((fb b //> fc) >>= \r -> Pure r) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- Definition of `(//>)` in reverse
= ((fb b //> fc) >>= \r -> Pure r //> \b -> fb b //> fc) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- Definition of `(//>)` in reverse
= (Respond b Pure //> \b -> fb b //> fc) >>= \b' -> fb' b' //> \b -> fb b //> fc
-- [Respond Category - Distributivity Law - Pointful], in reverse
= (Respond b Pure >>= \b' -> fb' b') //> \b -> fb b //> fc
-- Eta reduce
= (Respond b Pure >>= fb') //> \b -> fb b //> fc
-- Definition of `(>>=)`
= Respond b (\b' -> Pure b' >>= fb') //> \b -> fb b //> fc
-- Definition of `(>>=)`
= Respond b (\b' -> fb' b') //> \b -> fb b //> fc
-- Eta reduce
= Respond b fb' //> \b -> fb b //> fc
M m -> M (m >>= \p' -> return (p' //> fb)) //> fc
-- Definition of `(//>)`
= M (m >>= \p' -> return ((p' //> fb) //> fc))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (p' //> \b -> fb b //> fc))
-- Definition of `(//>)`, in reverse
= M m //> \b -> fb b //> fc
Pure a' = Pure a' //> fc
-- Definition of `(//>)`
= Pure a'
-- Definition of `(//>)`, in reverse
= Pure a' //> \b -> fb b //> fc
-- Clean up
= case p of
Request x' fx -> Request x' fx //> \b -> fb b //> fc
Respond b fb' -> Respond b fb' //> \b -> fb b //> fc
M m -> M m //> \b -> fb b //> fc
Pure a' -> Pure a' //> \b -> fb b //> fc
-- Factor from case statement
= (case p of
Request x' fx -> Request x' fx
Respond b fb' -> Respond b fb'
M m -> M m
Pure a' -> Pure a' ) //> \b -> fb b //> fc
-- case statement = id
= p //> \b -> fb b //> fc
-- Goal complete
## Distributivity Law
Goal: (k1 >=> k2) />/ fb = (k1 />/ fb) >=> (k2 />/ fb)
(k1 >=> k2) />/ fb
-- Definition of `(/>/)`
= \a -> ((k1 >=> k2) a) //> fb
-- Definition of `(>=>)`
= \a -> (k1 a >>= k2) //> fb
-- [Respond Category - Distributivity Law - Pointful]
= \a -> (k1 a //> fb) >>= \r -> k2 r //> fb
-- Definition of `(/>/)`, in reverse
= \a -> (k1 />/ fb) a >>= \r -> k2 r //> fb
-- Definition of `(/>/)`, in reverse
= \a -> (k1 />/ fb) a >>= (k2 />/ fb)
-- Definition of `(>=>)`, in reverse
= (k1 />/ fb) >=> (k2 />/ fb)
-- Goal complete
### Pointful
Goal: (p >>= k) //> fb = (p //> fb) >>= \r -> k r //> fb
(p >>= k) //> fb
-- Definition of `(>>=)`
= (case p of
Request x' fx -> Request x' (\x -> fx x >>= k)
Respond b fb' -> Respond b (\b' -> fb' b' >>= k)
M m -> M (m >>= \p' -> return (p' >>= k))
Pure r -> k r ) //> fb
-- Distribute over case statement
= case p of
Request x' fx -> Request x' (\x -> fx x >>= k) //> fb
-- Definition of `(//>)`
= Request x' (\x -> (fx x >>= k) //> fb)
-- Coinduction: Reuse the premise
= Request x' (\x -> (fx x //> fb) >>= \r -> k r //> fb)
-- Definition of `(>>=)`, in reverse
= Request x' (\x -> (fx x //> fb)) >>= \r -> k r //> fb
-- Definition of `(//>)`, in reverse
= (Request x' fx //> fb) >>= \r -> k r //> fb
Respond b fb' -> Respond b (\b' -> fb' b' >>= k) //> fb
-- Definition of `(//>)`
= fb b (\b' -> (fb' b' >>= k) //> fb)
-- Coinduction: Reuse the premise
= fb b >>= \b' -> (fb' b' //> fb) >>= \r -> k r //> fb
-- [Kleisli Category - Associativity Law - Pointful]
= (fb b >>= \b' -> fb' b' //> fb) >>= \r -> k r //> fb
-- Definition of `(//>)`, in reverse
= (Respond b fb' //> fb) >>= \r -> k r //> fb
M m -> M (m >>= \p' -> return (p >>= k)) //> fb
-- Definition of `(//>)`
= M (m >>= \p' -> return ((p >>= k) //> fb))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return ((p //> fb) >>= \r -> k r //> fb))
-- Definition of `(>>=)`, in reverse
= M (m >>= \p' -> return (p //> fb)) >>= \r -> k r //> fb
-- Definition of `(//>)`, in reverse
= (M m //> fb) >>= \r -> k r //> fb
Pure r -> k r //> fb
-- Definition of `(>>=)`, in reverse
= Pure r >>= \r -> k r //> fb
-- Definition of `(//>)`, in reverse
= (Pure r //> fb) >>= \r -> k r //> fb
-- Clean up
= case p of
Request x' fx -> (Request x' fx //> fb) >>= \r -> k r //> fb
Respond b fb' -> (Respond b fb' //> fb) >>= \r -> k r //> fb
M m -> (M m //> fb) >>= \r -> k r //> fb
Pure r -> (Pure r //> fb) >>= \r -> k r //> fb
-- Factor from case statement
= ((case p of
Request x' fx -> Request x' fx
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r ) //> fb) >>= \r -> k r //> fb
-- case statement = id
= (p //> fb) >>= \r -> k r //> fb
-- Goal complete
## Zero Law
Goal: return />/ f = return
return />/ f
-- Definition of `(/>/)`
= \r -> return r //> f
-- [Respond Category - Zero Law - Pointful]
= \r -> return r
-- Eta reduce
= return
-- Goal complete
### Pointful
Goal: return r //> f = return r
return r //> f
-- Definition of `return`
= Pure r //> f
-- Definition of `(//>)`
= Pure r
-- Definition of `return`, in reverse
= return r
-- Goal complete
# Request Category
Define:
request
:: (Monad m)
=> a' -> Proxy a' a y' y m a
request a' = Request a' Pure
(\>\)
:: (Monad m)
=> (b' -> Proxy a' a y' y m b)
-> (c' -> Proxy b' b y' y m c)
-> (c' -> Proxy a' a y' y m c)
(fb' \>\ fc') c' = fb' >\\ fc' c'
(>\\)
:: (Monad m)
=> (b' -> Proxy a' a y' y m b)
-> Proxy b' b y' y m c
-> Proxy a' a y' y m c
fb' >\\ p = case p of
Request b' fb -> fb' b' >>= \b -> fb' >\\ fb b
Respond y fy' -> Respond y (\y' -> fb' >\\ fy' y')
M m -> M (m >>= \p' -> return (fb' >\\ p'))
Pure c -> Pure c
## Left Identity Law
Goal: request \>\ fc' = fc'
request \>\ fc' = fc'
-- Definition of `(\>\)`
= \c' -> request >\\ fc' c'
-- [Request Category - Left Identity Law - Pointful]
= \c' -> fc' c'
-- Eta reduce
= fc'
-- Goal complete
### Pointful
Goal: request >\\ p = p
-- Definition of `(>\\)`
case p of
Request b' fb -> request b' >>= \b -> request >\\ fb b
-- Coinduction: Reuse the premise
= request b' >>= \b -> fb b
-- Eta reduce
= request b' >>= fb
-- Definition of `request`
= Request b' Pure >>= fb
-- Definition of `(>>=)`
= Request b' (\b -> Pure b >>= fb)
-- Definition of `(>>=)`
= Request b' (\b -> fb b)
-- Eta reduce
= Request b' fb
Respond y fy' -> Respond y (\y' -> request >\\ fy' y')
-- Coinduction: Reuse the premise
= Respond y (\y' -> fy' y')
-- Eta reduce
= Respond y fy'
M m -> M (m >>= \p' -> return (request >\\ p'))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p')
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful]
= M m
Pure c = Pure c
-- Clean up
= case p of
Request b' fb -> Request b' fb
Respond y fy' -> Respond y fy'
M m -> M m
Pure c -> Pure c
-- case statement = if
= p
-- Goal complete
## Right Identity Law
Goal: fb' \>\ request = fb'
fb' \>\ request
-- Definition of `(\>\)`
= \b' -> fb' >\\ request b'
-- [Request Category - Right Identity Law - Pointful]
= \b' -> fb' b'
-- Eta reduce
= fb'
-- Goal complete
### Pointful
Goal: fb' >\\ request b' = fb' b'
fb' >\\ request b'
-- Definition of `request`
= fb' >\\ Request b' Pure
-- Definitoin of `(>\\)`
= fb' b' >>= \b -> fb' >\\ Pure b
-- Definition of `(>\\)`
= fb' b' >>= \b -> Pure b
-- Eta reduce
= fb' b' >>= Pure
-- Definition of `return`, in reverse
= fb' b' >>= return
-- [Kleisli Category - Right Identity Law - Pointful]
= fb' b'
-- Goal complete
## Associativity Law
Goal: (f \>\ g) \>\ h = f \>\ (g \>\ h)
(f \>\ g) \>\ h
-- Definition of `(\>\)`
= \x -> (f \>\ g) >\\ h x
-- Definition of `(\>\)`
= \x -> (\y -> f >\\ g y) >\\ h x
-- [Request Category - Composition - Pointful]
= \x -> f >\\ (g >\\ h x)
-- Definition of `(\>\)`, in reverse
= \x -> f >\\ (g \>\ h) x
-- Definition of `(\>\)`, in reverse
= f \>\ (g \>\ h)
-- Goal complete
### Pointful
Goal: fa' >\\ (fb' >\\ p) = (\b' -> fa' >\\ fb' b') >\\ p
fa' >\\ (fb' >\\ p)
-- Definition of `(>\\)`
= fa' >\\ (case p of
Request b' fb -> fb' b' >>= \b -> fb' >\\ fb b
Respond y fy' -> Respond y (\y' -> fb' >\\ fy' y')
M m -> M (m >>= \p' -> return (fb' >\\ p'))
Pure c -> Pure c
-- Distribute over case statement
= case p of
Request b' fb -> fa' >\\ (fb' b' >>= \b -> fb' >\\ fb b)
-- [Request Category - Distributivity Law]
= (fa' >\\ fb' b') >>= \b -> fa' >\\ (fb' >\\ fb b)
-- Coinduction : Reuse the premise
= (fa' >\\ fb' b') >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- [Kleisli Category - Right Identity Law - Pointful], in reverse
= ((fa' >\\ fb' b') >>= return) >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- Definition of `return`
= ((fa' >\\ fb' b') >>= Pure) >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- Eta expand
= ((fa' >\\ fb' b') >>= \r -> Pure r) >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- Definition of `(>\\)`, in reverse
= ((fa' >\\ fb' b') >>= \r -> (\b' -> fa' >\\ fb' b') >\\ Pure r) >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- Definition of `(>\\)`, in reverse
= ((\b' -> fa' >\\ fb' b') >\\ Request b' Pure) >>= \b -> (\b' -> fa' >\\ fb' b') >\\ fb b
-- [Request Category - Distributivity Law - Pointful], in reverse
= (\b' -> fa' >\\ fb' b') >\\ (Request b' Pure >>= fb)
-- Definition of `(>>=)`
= (\b' -> fa' >\\ fb' b') >\\ Request b' (\b -> Pure b >>= fb)
-- Definition of `(>>=)`
= (\b' -> fa' >\\ fb' b') >\\ Request b' (\b -> fb b)
-- Eta reduce
= (\b' -> fa' >\\ fb' b') >\\ Request b' fb
Respond y fy' -> fa' >\\ Respond y (\y' -> fb' >\\ fy' y')
-- Definition of `(>\\)`
= Respond y (\y' -> fa' >\\ (fb' >\\ fy' y'))
-- Coinduction: Reuse the premise
= Respond y (\y' -> (\b' -> fa' >\\ fb' b') >\\ fy' y')
-- Definition of `(>\\)`, in reverse
= (\b' -> fa' >\\ fb' b') >\\ Respond y fy'
M m -> fa' >\\ M (m >>= \p' -> return (fb' >\\ p'))
-- Definition of `(>\\)`
= M (m >>= \p' -> return (fa' >\\ (fb' >\\ p')))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return ((\b' -> fa' >\\ fb' b') >\\ p'))
-- Definition of `(>\\)`, in reverse
= (\b' -> fa' >\\ fb' b') >\\ M m
Pure c = fa' >\\ Pure c
-- Definition of `(>\\)`
= Pure c
-- Definition of `(>\\)`, in reverse
= (\b' -> fa' >\\ fb' b') >\\ Pure c
-- Clean up
= case p of
Request b' fb -> (\b' -> fa' >\\ fb' b') >\\ Request b' fb
Request y fy' -> (\b' -> fa' >\\ fb' b') >\\ Respond y fy'
M m -> (\b' -> fa' >\\ fb' b') >\\ M m
Pure c -> (\b' -> fa' >\\ fb' b') >\\ Pure c
-- Factor from case statement
= (\b' -> fa' >\\ fb' b') >\\ (case p of
Request b' fb -> Request b' fb
Respond y fy' -> Respond y fy'
M m -> M m
Pure c -> Pure c )
-- case statement = id
= (\b' -> fa' >\\ fb' b') >\\ p
-- Goal complete
## Distributivity Law
Goal: fb' \>\ (k1 >=> k2) = (fb' \>\ k1) >=> (fb' \>\ k2)
fb' \>\ (k1 >=> k2)
-- Definition of `(\>\)`
= \x -> fb' >\\ ((k1 >=> k2) x)
-- Definition of `(>=>)`
= \x -> fb' >\\ (k1 x >>= k2)
-- [Request Category - Distributivity Law - Pointful]
= \x -> (fb' >\\ k1 x) >>= \y -> fb' >\\ k2 y
-- Definition of `(\>\)`, in reverse
= \x -> (fb' >\\ k1 x) >>= (fb' \>\ k2)
-- Definition of `(\>\)`, in reverse
= \x -> (fb' \>\ k1) x >>= (fb' \>\ k2)
-- Definition of `(>=>)`, in reverse
= (fb' \>\ k1) >=> (fb' \>\ k2)
-- Goal complete
### Pointful
Goal: fb' >\\ (p >>= k) = (fb' >\\ p) >>= \r -> fb' >\\ k r
fb' >\\ (p >>= k)
-- Definition of `(>>=)`
= fb' >\\ (case p of
Request b' fb -> Request b' (\b -> fb b >>= k)
Respond y fy' -> Respond y (\y' -> fy' y' >>= k)
M m -> M (m >>= \p' -> return (p' >>= k))
Pure c -> k c )
-- Distribute over case statement
= case p of
Request b' fb -> fb' >\\ Request b' (\b -> fb b >>= k)
-- Definition of `(>\\)`
= fb' b' >>= \b -> fb' >\\ (fb b >>= k)
-- Coinduction: Reuse the premise
= fb' b' >>= \b -> (fb' >\\ fb b) >>= \r -> fb' >\\ k r
-- [Kleisli Category -- Associativity Law - Pointful]
= (fb' b' >>= \b -> fb' >\\ fb b) >>= \r -> fb' >\\ k r
-- Definition of `(>\\)`, in reverse
= (fb' >\\ Request b' fb) >>= \r -> fb' >\\ k r
Respond y fy' -> fb' >\\ Respond y (\y' -> fy' y' >>= k)
-- Definition of `(>\\)`
= Respond y (\y' -> fb' >\\ (fy' y' >>= k))
-- Coinduction: Reuse the premise
= Respond y (\y' -> (fb' >\\ fy' y') >>= \r -> fb' >\\ k r)
-- Definition of `(>>=)`, in reverse
= Respond y (\y' -> (fb' >\\ fy' y')) >>= \r -> fb' >\\ k r
-- Definition of `(>\\)`, in reverse
= (fb' >\\ Respond y fy') >>= \r -> fb' >\\ k r
M m -> fb' >\\ M (m >>= \p' -> return (p' >>= k))
-- Definition of `(>\\)`
= M (m >>= \p' -> return (fb' >\\ (p' >>= k)))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return ((fb' >\\ p') >>= \r -> fb' >\\ k r))
-- Definition of `(>>=)`, in reverse
= M (m >>= \p' -> return (fb' >\\ p')) >>= \r -> fb' >\\ k r
-- Definition of `(>\\)`, in reverse
= (fb' >\\ M m) >>= \r -> fb' >\\ k r
Pure c = fb' >\\ k c
-- [Kleisli Category - Left Identity Law - Pointful], in reverse
= return c >>= \r -> fb' >\\ k r
-- Definition of `return`
= Pure c >>= \r -> fb' >\\ k r
-- Definition of `(>\\)`, in reverse
= (fb' >\\ Pure c) >>= \r -> fb' >\\ k r
-- Clean up
= case p of
Request b' fb -> (fb' >\\ Request b' fb ) >>= \r -> fb' >\\ k r
Respond y fy' -> (fb' >\\ Respond y fy') >>= \r -> fb' >\\ k r
M m -> (fb' >\\ M m ) >>= \r -> fb' >\\ k r
Pure c -> (fb' >\\ Pure c ) >>= \r -> fb' >\\ k r
-- Factor from case statement
= (fb' >\\ case p of
Request b' fb -> Request b' fb
Respond y fy' -> Respond y fy'
M m -> M m
Pure c -> Pure c ) >>= \r -> fb' >\\ k r
-- case statement = id
= (fb' >\\ p) >>= \r -> fb' >\\ k r
-- Goal complete
## Zero Law
Goal: f \>\ return = return
f \>\ return
-- Definition of `(\>\)`
= \r -> f >\\ return r
-- [Request Category - Zero Law - Pointful]
= \r -> return r
-- Eta reduce
= return
-- Goal complete
### Pointful
Goal: f >\\ return r = return r
f >\\ return r
-- Definition of `return`
= f >\\ Pure r
-- Definition of `(>\\)`
= Pure r
-- Definition of `return`, in reverse
= return r
-- Goal complete
# Pull Category
Define:
pull
:: (Monad m)
=> a' -> Proxy a' a a' a m r
pull a' = Request a' push
(>+>)
:: (Monad m)
=> ( b' -> Proxy a' a b' b m r)
-> (_c' -> Proxy b' b c' c m r)
-> (_c' -> Proxy a' a c' c m r)
(fb' >+> fc') c' = fb' +>> fc' c'
(+>>)
:: (Monad m)
=> ( b' -> Proxy a' a b' b m r)
-> Proxy b' b c' c m r
-> Proxy a' a c' c m r
fb' +>> p = case p of
Request b' fb -> fb' b' >>~ fb
Respond c fc' -> Respond c (\c' -> fb' +>> fc' c')
M m -> M (m >>= \p' -> return (fb' +>> p'))
Pure r -> Pure r
## Left Identity Law
Goal: pull >+> f = f
pull >+> f
-- Definition of `(>+>)`
= \c' -> pull +>> f c'
-- [Pull Category - Left Identity Law - Pointful]
= \c' -> f c'
-- Eta reduce
= f
-- Goal complete
### Pointful
Goal: pull +>> p = p
pull +>> p
-- Definition of `(+>>)`
= case p of
Request b' fb -> pull b' >>~ fb
-- Definition of `pull`
= Request b' (\b -> Respond b pull) >>~ fb
-- Definition of `(>>~)`
= Request b' (\b -> Respond b pull >>~ fb)
-- Definition of `(>>~)`
= Request b' (\b -> pull +>> fb b)
-- Coinduction: Reuse the premise
= Request b' (\b -> fb b)
-- Eta reduce
= Request b' fb
Respond c fc' -> Respond c (\c' -> pull +>> fc' c')
-- Coinduction: Reuse the premise
= Respond c (\c' -> fc' c')
-- Eta reduce
= Respond c fc'
M m -> M (m >>= \p' -> return (pull +>> p'))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p')
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful]
= M m
Pure r -> Pure r
-- Clean up
= case p of
Request b' fb -> Request b' fb
Respond c fc' -> Respond c fc'
M m -> M m
Pure r -> Pure r
-- case statement = id
= p
-- Goal complete
## Right Identity Law
Goal: fb' >+> pull = fb'
fb' >+> pull
-- Definition of `(>+>)`
= \b' -> fb' +>> pull b'
-- [Pull Category - Right Identity Law - Pointful]
= \b' -> fb' b'
-- Eta reduce
= fb'
-- Goal complete
### Pointful
Goal: fb' +>> pull b' = fb' b'
fb' +>> pull b'
-- Definition of `push`
= fb' +>> Request b' (\b -> Respond b pull)
-- Definition of `(+>>)`
= fb' b' >>~ \b -> Respond b pull
-- Definition of `push`, in reverse
= fb' b' >>~ push
-- Coinduction: [Push Category - Right Identity Law - Pointful]
= fb' b'
-- Goal complete
## Associativity Law
Goal: fb' >+> (fc' >+> fd') = (fb' >+> fc') >+> fd'
fb' >+> (fc' >+> fd')
-- Definition of `(>+>)`
= \d' -> fb' +>> (fc' >+> fd') d'
-- Definition of `(>+>)`
= \d' -> fb' +>> (fc' +>> fd' d')
-- [Pull Category - Associativity Law - Pointful]
= \d' -> (\c' -> fb' +>> fc' c') +>> fd' d'
-- Definition of `(>+>)`, in reverse
= \d' -> (fb' >+> fc') +>> fd' d'
-- Definition of `(>+>)`, in reverse
= (fb' >+> fc') >+> fd'
-- Goal complete
### Pointful
Goal: fb' +>> (fc' +>> p) = (\c' -> fb' +>> fc' c') +>> p
fb' +>> (fc' +>> p)
-- Definition of `(+>>)`
= fb' +>> (case p of
Request c' fc -> fc' c' >>~ fc
Respond d fd' -> Respond d (\d' -> fc' +>> fd' d')
M m -> M (m >>= \p' -> return (fc' +>> p'))
Pure r -> Pure r )
-- Distribute over case statement
= case p of
Request c' fc -> fb' +>> (fc' c' >>~ fc)
-- Coinduction: [Push/Pull - Associativity - Pointful]
= (fb' +>> fc' c') >>~ fc
-- Definition of `(+>>), in reverse
= (\c' -> fb' +>> fc' c') +>> Request c' fc
Respond d fd' -> fb' +>> Respond d (\d' -> fc' +>> fd' d')
-- Definition of `(+>>)`
= Respond d (\d' -> fb' +>> (fc' +>> fd' d'))
-- Coinduction: Reuse the premise
= Respond d (\d' -> (\c' -> fb' +>> fc' c') +>> fd' d')
-- Definition of `(+>>)`, in reverse
= (\c' -> fb' +>> fc' c') +>> Respond d fd'
M m -> fb' +>> M (m >>= \p' -> return (fc' +>> p'))
-- Definition of `(+>>)`
= M (m >>= \p' -> return (fb' +>> (fc' +>> p')))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return ((\c' -> fb' +>> fc' c') +>> p'))
-- Definition of `(+>>)`, in reverse
= (\c' -> fb' +>> fc' c') +>> M m
Pure r -> fb' +>> Pure r
-- Definition of `(+>>)`
= Pure r
-- Definition of `(+>>)`, in reverse
= (\c' -> fb' +>> fc' c') +>> Pure r
-- Clean up
= case p of
Request c' fc -> (\c' -> fb' +>> fc' c') +>> Request c' fc
Respond d fd' -> (\c' -> fb' +>> fc' c') +>> Respond d fd'
M m -> (\c' -> fb' +>> fc' c') +>> M m
Pure r -> (\c' -> fb' +>> fc' c') +>> Pure r
-- Factor from case statement
= (\c' -> fb' +>> fc' c') +>> (case p of
Request c' fc -> Request c' fc
Respond d fd' -> Respond d fd'
M m -> M m
Pure r -> Pure r )
-- case statement = id
= (\c' -> fb' +>> fc' c') +>> p
-- Goal complete
# Push Category
Define:
push
:: (Monad m)
=> a -> Proxy a' a a' a m r
push a = Respond a pull
(>~>)
:: (Monad m)
=> (_a -> Proxy a' a b' b m r)
-> ( b -> Proxy b' b c' c m r)
-> (_a -> Proxy a' a c' c m r)
(f >~> g) x = f x >>~ g
(>>~)
:: (Monad m)
=> Proxy a' a b' b m r
-> ( b -> Proxy b' b c' c m r)
-> Proxy a' a c' c m r
p >>~ fb = case p of
Request a' fa -> Request a' (\a -> fa a >>~ fb)
Respond b fb' -> fb' +>> fb b
M m -> M (m >>= \p' -> return (p' >>~ fb))
Pure r -> Pure r
## Left Identity Law
Goal: push >~> f = f
push >~> f
-- Definition of `(>~>)`
= \a -> push a >>~ f
-- [Push Category - Left Identity Law - Pointful]
= \a -> f a
-- Eta reduce
= f
-- Goal complete
### Pointful
Goal: push a >>~ f = f a
push a >>~ f
-- Definition of `push`
= Respond a pull >>~ f
-- Definition of `(>>~)`
= pull +>> f a
-- Coinduction: [Pull Category - Left Identity Law - Pointful]
= f a
-- Goal complete
## Right Identity Law
Goal: f >~> push = f
f >~> push
-- Definition of `(>~>)`
= \a -> f a >>~ push
-- [Push Category - Right Identity Law - Pointful]
= \a -> f a
-- Eta reduce
= f
-- Goal complete
### Pointful
Goal: p >>~ push = p
p >>~ push
-- Definition of `(>>~)`
= case p of
Request a' fa -> Request a' (\a -> fa a >>~ push)
-- Coinduction: Reuse the premise
= Request a' (\a -> fa a)
-- Eta reduce
= Request a' fa
Respond b fb' -> fb' +>> push b
-- Definition of `push`
= fb' +>> Respond b pull
-- Definition of `(+>>)`
= Respond b (fb' +>> pull)
-- [Pull Category - Right Identity Law - Pointful]
= Respond b fb'
M m -> M (m >>= \p' -> return (p >>~ push))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p)
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful]
= M m
Pure r -> Pure r
-- Clean up
= case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r
-- case statement = id
= p
-- Goal complete
## Associativity Law
Goal: (fa >~> fb) >~> fc = fa >~> (fb >~> fc)
(fa >~> fb) >~> fc
-- Definition of `(>~>)`
= \a -> (fa >~> fb) a >>~ fc
-- Definition of `(>~>)`
= \a -> (fa a >>~ fb) >>~ fc
-- [Push Category - Associativity Law - Pointful]
= \a -> fa a >>~ \b -> fb b >>~ fc
-- Definition of `(>~>)`, in reverse
= \a -> fa a >>~ (fb >~> fc)
-- Definition of `(>~>)`, in reverse
= fa >~> (fb >~> fc)`
-- Goal complete
### Pointful
Goal: (p >>~ fb) >>~ fc = p >>~ \b -> fb b >>~ fc
(p >>~ fb) >>~ fc
-- Definition of `(>>~)`
= (case p of
Request a' fa -> Request a' (\a -> fa a >>~ fb)
Respond b fb' -> fb' +>> fb b
M m -> M (m >>= \p' -> return (p' >>~ fb))
Pure r -> Pure r ) >>~ fc
-- Distribute over case statement
= case p of
Request a' fa -> Request a' (\a -> fa a >>~ fb) >>~ fc
-- Definition of `(>>~)`
= Request a' (\a -> (fa a >>~ fb) >>~ fc)
-- Coinduction: Reuse the premise
= Request a' (\a -> fa a >>~ \b -> fb b >>~ fc)
-- Definition of `(>>~), in reverse
= Request a' fa >>~ \b -> fb b >>~ fc
Respond b fb' -> (fb' +>> fb b) >>~ fc
-- Coinduction: [Push/Pull - Associativity - Pointful]
= fb' +>> (fb b >>~ fc)
-- Definition of `(>>~)`, in reverse
= Respond b fb' >>~ \b -> fb b >>~ fc
M m -> M (m >>= \p' -> return (p' >>~ fb)) >>~ fc
-- Definition of `(>>~)`
= M (m >>= \p' -> return ((p >>~ fb) >>~ fc))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (p >>~ \b -> fb b >>~ fc))
-- Definition of `(>>~), in reverse
= M m >>~ \b -> fb b >>~ fc
Pure r -> Pure r >>~ fc
-- Definition of `(>>~)`
= Pure r
-- Definition of `(>>~)`, in reverse
= Pure r >>~ \b -> fb b >>~ fc
-- Clean up
= case p of
Request a' fa -> Request a' fa >>~ \b -> fb b >>~ fc
Respond b fb' -> Respond b fb' >>~ \b -> fb b >>~ fc
M m -> M m >>~ \b -> fb b >>~ fc
Pure r -> Pure r >>~ \b -> fb b >>~ fc
-- Factor from case statement
= (case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r ) >>~ \b -> fb b >>~ fc
-- case statement = id
= p >>~ \b -> fb b >>~ fc
-- Goal complete
# Push/Pull
## Associativity
Goal: (f >+> g) >~> h = f >+> (g >~> h)
(f >+> g) >~> h
-- Definition of `(>~>)`
= \x -> (f >+> g) x >>~ h
-- Definition of `(>+>)`
= \x -> (f +>> g x) >>~ h
-- [Push/Pull - Associativity - Pointful]
= \x -> f +>> (g x >>~ h)
-- Definition of `(>~>)`, in reverse
= \x -> f +>> (g >~> h)x
-- Definition of `(>+>)`, in reverse
= f >+> (g >~> h)
-- Goal complete
### Pointful
Goal: (fb' +>> p) >>~ fc = fb' +>> (p >>~ fc)
(fb' +>> p) >>~ fc
-- Definition of `(+>>)`
= (case p of
Request b' fb -> fb' b' >>~ fb
Respond c fc' -> Respond c (\c' -> fb' +>> fc' c')
M m -> M (m >>= \p' -> return (fb' +>> p'))
Pure r -> Pure r ) >>~ fc
-- Distribute over case statement
= case p of
Request b' fb -> (fb' b' >>~ fb) >>~ fc
-- Coinduction: [Push Category - Associativity Law - Pointful]
= fb' b' >>~ \b -> fb b >>~ fc
-- Definition of `(+>>)`, in reverse
= fb' +>> Request b' (\b -> fb b >>~ fc)
-- Definition of `(>>~)`, in reverse
= fb' +>> (Request b' fb >>~ fc)
Respond c fc' -> Respond c (\c' -> fb' +>> fc' c') >>~ fc
-- Definition of `(>>~)`
= (\c' -> fb' +>> fc' c') +>> fc c
-- Coinduction: [Pull Category - Associativity Law - Pointful]
= fb' +>> (fc' +>> fc c)
-- Definition of `(>>~)`, in reverse
= fb' +>> (Respond c fc' >>~ fc)
M m -> M (m >>= \p' -> return (fb' +>> p')) >>~ fc
-- Definition of `(>>~)`
= M (m >>= \p' -> return ((fb' +>> p') >>~ fc))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (fb' +>> (p' >>~ fc))
-- Definition of `(+>>)`, in reverse
= fb' +>> M (m >>= \p' -> return (p' >>~ fc))
-- Definition of `(>>~)`, in reverse
= fb' +>> (M m >>~ fc)
Pure r -> Pure r >>~ fc
-- Definition of `(+>>)`, in reverse
= fb' +>> (Pure r >>~ fc)
-- Clean up
= case p of
Request b' fb -> fb' +>> (Request b' fb >>~ fc)
Respond c fc' -> fb' +>> (Respond c fc' >>~ fc)
M m -> fb' +>> (M m >>~ fc)
Pure r -> fb' +>> (Pure r >>~ fc)
-- Factor from case statement
= fb' +>> ((case p of
Request b' fb -> Request b' fb
Respond c fc' -> Respond c fc'
M m -> M m
Pure r -> Pure r ) >>~ fc)
-- case statement = id
= fb' +>> (p >>~ fc)
-- Goal complete
# Duals
Define:
reflect
:: (Monad m)
=> Proxy a' a b' b m r
-> Proxy b b' a a' m r
reflect p = case p of
Request a' fa -> Respond a' (\a -> go (fa a ))
Respond b fb' -> Request b (\b' -> go (fb' b'))
M m -> M (m >>= \p' -> return (go p'))
Pure r -> Pure r
## Request Identity
Goal: reflect . request = respond
reflect . request
-- Definition of `(.)`
= \a' -> reflect (request a')
-- [Dual - Request Identity - Pointful]
= \a' -> respond a'
-- Eta reduce
= respond
-- Goal complete
### Pointful
Goal: reflect (request x) = respond x
reflect (request x)
-- Definition of `request`
= reflect (Request x Pure)
-- Definition of `reflect`
= Respond x (\r -> reflect (Pure r))
-- Definition of `reflect`
= Respond x (\r -> Pure r)
-- Eta reduce
= Respond x Pure
-- Definition of `respond`, in reverse
= respond
-- Goal complete
## Request Composition
Goal: reflect . (f \>\ g) = reflect . g />/ reflect . f
reflect . (f \>\ g)
-- Definition of `(.)`
= \a -> reflect ((f \>\ g) a)
-- Definition of `(\>\)`
= \a -> reflect (f >\\ g a)
-- [Dual - Request Composition - Pointful]
= \a -> reflect (g a) //> reflect . f
-- Definition of `(.)`, in reverse
= \a -> (reflect . g) a //> reflect . f
-- Definition of `(/>/)`, in reverse
= reflect . g />/ reflect . f
-- Goal complete
### Pointful
Goal: reflect (f >\\ p) = reflect p //> reflect . f
reflect (f >\\ p)
-- Definition of `(>\\)`
= reflect (case p of
Request b' fb -> f b' >>= \b -> f >\\ f b
Respond x fx' -> Respond x (\x' -> f >\\ fx' x')
M m -> M (m >>= \p' -> return (f >\\ p'))
Pure c -> Pure c )
-- Distribute over case statement
= case p of
Request b' fb -> reflect (f b' >>= \b -> f >\\ fb b)
-- [Dual - Distributivity Law - Pointful]
= reflect (f b') >>= \b -> reflect (f >\\ fb b)
-- Coinduction: Reuse the premise
= reflect (f b') >>= \b -> reflect (fb b) //> reflect . f
-- Definition of `(.)`, in reverse
= (reflect . f) b' >>= \b -> reflect (fb b) //> reflect . f
-- Definition of `(//>)`, in reverse
= Respond b' (\b -> reflect (fb b)) //> reflect . f
-- Definition of `reflect`, in reverse
= reflect (Request b' fb) //> reflect . f
Respond x fx' -> reflect (Respond x (\x' -> f >\\ fx' x'))
-- Definition of `reflect`
= Request x (\x' -> reflect (f >\\ fx' x'))
-- Coinduction: Reuse the premise
= Request x (\x' -> reflect (fx' x') //> reflect . f)
-- Definition of `(//>)`, in reverse
= Request x (\x' -> reflect (fx' x')) //> reflect . f
-- Definition of `reflect`, in reverse
= reflect (Respond x fx') //> reflect . f
M m -> reflect (M (m >>= \p' -> return (f >\\ p')))
-- Definition of `reflect`
= M (m >>= \p' -> return (reflect (f >\\ p')))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (reflect p' //> reflect . f))
-- Definition of `(//>)`, in reverse
= M (m >>= \p' -> return (reflect p')) //> reflect . f
-- Definition of `reflect, in reverse
= reflect (M m) //> reflect . f
Pure c = reflect (Pure c)
-- Definition of `reflect
= Pure c
-- Definition of `(//>)`, in reverse
= Pure c //> reflect . f
-- Definition of `reflect`, in reverse
= reflect (Pure c) //> reflect .f
-- Clean up
= case p of
Request b' fb -> reflect (Request b' fb ) //> reflect . f
Respond x fx' -> reflect (Respond x fx') //> reflect . f
M m -> reflect (M m ) //> reflect . f
Pure c -> reflect (Pure c ) //> reflect . f
-- Factor from case statement
= reflect (case p of
Request b' fb -> Request b' fb
Respond x fx' -> Respond x fx'
M m -> M m
Pure c -> Pure c ) //> reflect . f
-- case statement = id
= reflect p //> reflect . f
-- Goal complete
## Respond Identity
Goal: reflect . respond = request
reflect . respond
-- Definition of `(.)`
= \a -> reflect (respond a)
-- [Dual - Respond Identity - Pointful]
= \a -> request a
-- Eta reduce
= request
-- Goal complete
### Pointful
Goal: reflect (respond x) = request x
reflect (respond x)
-- Definition of `respond`
= reflect (Respond x Pure)
-- Definition of `reflect`
= Request x (\r -> reflect (Pure r))
-- Definition of `reflect`
= Request x (\r -> Pure r)
-- Eta reduce
= Request x Pure
-- Definition of `request`, in reverse
= request
-- Goal complete
## Respond Composition
Goal: reflect . (f />/ g) = reflect . g \>\ reflect . f
reflect . (f />/ g)
-- Definition of `(.)`
= \x -> reflect ((f />/ g) x)
-- Definition of `(/>/)`
= \x -> reflect (f x //> g)
-- [Dual - Respond Composition - Pointful]
= \x -> reflect . g >\\ reflect (f x)
-- Definition of `(.)`, in reverse
= \x -> reflect . g >\\ (reflect . f) x
-- Definition of `(\>\)`, in reverse
= reflect . g \>\ reflect . f
-- Goal complete
### Pointful
Goal: reflect (p //> f) = reflect . f >\\ reflect p
reflect (p //> f)
-- Definition of `(//>)`
= reflect (case p of
Request x' fx -> Request x' (\x -> fx x //> f)
Respond b fb' -> f b >>= \b' -> fb' b' //> f
M m -> M (m >>= \p' -> return (p' //> f))
Pure a' -> Pure a' )
-- Distribute over case statement
= case p of
Request x' fx -> reflect (Request x' (\x -> fx x //> f))
-- Definition of `reflect`
= Respond x' (\x -> reflect (fx x //> f))
-- Coinduction: Reuse the premise
= Respond x' (\x -> reflect . f >\\ reflect (fx x))
-- Definition of `(>\\)`, in reverse
= reflect . f >\\ Respond x' (\x -> reflect (fx x))
-- Definition of `reflect`, in reverse
= reflect . f >\\ reflect (Request x' fx)
Respond b fb' -> reflect (f b >>= \b' -> fb' b' //> f)
-- [Dual - Distributivity Law - Pointful]
= reflect (f b) >>= \b' -> reflect (fb' b' //> f)
-- Coinduction: Reuse the premise
= reflect (f b) >>= \b' -> reflect . f >\\ reflect (fb' b')
-- Definition of `(.)`
= (reflect . f) b >>= \b' -> reflect . f >\\ reflect (fb' b')
-- Definition of `(>\\)`, in reverse
= reflect . f >\\ (Request b (\b' -> reflect (fb' b')))
-- Definition of `reflect`, in reverse
= reflect . f >\\ reflect (Respond b fb')
M m = reflect (M (m >>= \p' -> return (p' //> f)))
-- Definition of `reflect`
= M (m >>= \p' -> return (reflect (p' //> f)))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (reflect . f >\\ reflect p'))
-- Definition of `(>\\)`, in reverse
= reflect . f >\\ M (m >>= \p' -> return (reflect p'))
-- Definition of `reflect`, in reverse
= reflect . f >\\ reflect (M m)
Pure a' = reflect (Pure a')
-- Definition of `reflect`
= Pure a'
-- Definition of `(>\\)`, in reverse
= reflect . f >\\ Pure a'
-- Definition of `reflect`, in reverse
= reflect . f >\\ reflect (Pure a')
-- Clean up
= case p of
Request x' fx -> reflect . f >\\ reflect (Request x' fx )
Respond b fb' -> reflect . f >\\ reflect (Respond b fb')
M m -> reflect . f >\\ reflect (M m )
Pure a' -> reflect . f >\\ reflect (Pure a' )
-- Factor from case statement
= reflect . f >\\ reflect (case p of
Request x' fx -> Request x' fx
Respond b fb' -> Respond b fb'
M m -> M m
Pure a' -> Pure a' )
-- case statement = id
= reflect . f >\\ reflect p
-- Goal complete
## Distributivity Law
Goal: reflect . (f >=> g) = reflect . f >=> reflect . g
reflect . (f >=> g)
-- Definition of `(.)`
= \x -> reflect ((f >=> g) x)
-- Definition of `(>=>)`
= \x -> reflect (f x >>= g)
-- [Dual - Distributive Law - Pointful]
= \x -> reflect (f x) >>= \y -> reflect (g y)
-- Definition of `(.)`, in reverse
= \x -> reflect (f x) >>= reflect . g
-- Definition of `(.)`, in reverse
= \x -> ((reflect . f) x >>= reflect . g)
-- Definition of `(>=>)`, in reverse
= reflect . f >=> reflect . g
-- Goal complete
### Pointful
Goal: reflect (p >>= f) = reflect p >>= \x -> reflect (f x)
reflect (p >>= f)
-- Definition of `(>>=)`
= reflect (case p of
Request a' fa -> Request a' (\a -> fa a >>= f)
Respond b fb' -> Respond b (\b' -> fb' b' >>= f)
M m -> M (m >>= \p' -> return (p' >>= f))
Pure r -> f r )
-- Distribute over case statement
= case p of
Request a' fa -> reflect (Request a' (\a -> fa a >>= f))
-- Definition of `reflect`
= Respond a' (\a -> reflect (fa a >>= f))
-- Coinduction: Reuse the premise
= Respond a' (\a -> reflect (fa a) >>= \x -> reflect (f x))
-- Definition of `(>>=)`, in reverse
= Respond a' (\a -> reflect (fa a)) >>= \x -> reflect (f x)
-- Definition of `reflect`, in revrse
= reflect (Request a' (\a -> fa a)) >>= \x -> reflect (f x)
-- Eta reduce
= reflect (Request a' fa) >>= \x -> reflect (f x)
Respond b fb' -> reflect (Respond b (\b' -> fb' b' >>= f))
-- Definition of `reflect`
= Request b (\b' -> reflect (fb' b' >>= f))
-- Coinduction: Reuse the premise
= Request b (\b' -> reflect (fb' b') >>= \x -> reflect (f x))
-- Definition of `(>>=)`, in reverse
= Request b (\b' -> reflect (fb' b')) >>= \x -> reflect (f x)
-- Definition of `reflect`, in reverse
= reflect (Respond b (\b' -> fb' b')) >>= \x -> reflect (f x)
-- Eta reduce
= reflect (Respond b fb') >>= \x -> reflect (f x)
M m -> reflect (M (m >>= \p' -> return (p' >>= f)))
-- Definition of `reflect`
= M (m >>= \p' -> return (reflect (p' >>= f)))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return (reflect p' >>= \x -> reflect (f x)))
-- Definition of `(>>=)`, in reverse
= M (m >>= \p' -> return (reflect p')) >>= \x -> reflect (f x)
-- Definition of `reflect`, in reverse
= reflect (M m) >>= \x -> reflect (f x)
Pure r -> reflect (f r)
-- [Kleisli Category - Left Identity - Pointful]
reflect (return r >>= f)
-- Coinduction: Reuse the premise
reflect (return r) >>= \x -> return (f x)
-- Definition of `return`
reflect (Pure r) >>= \x -> return (f x)
-- Cleanup
= case p of
Request a' fa -> reflect (Request a' fa ) >>= \x -> reflect (f x)
Respond b fb' -> reflect (Respond b fb') >>= \x -> reflect (f x)
M m -> reflect (M m ) >>= \x -> reflect (f x)
Pure r -> reflect (Pure r ) >>= \x -> reflect (f x)
-- Factor from case statement
= reflect (case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r ) >>= \x -> reflect (f x)
-- case statement = id
= reflect p >>= \x -> reflect (f x)
-- Goal complete
## Zero Law
Goal: reflect . return = return
reflect . return
-- Definition of `(.)`
= \r -> reflect (return r)
-- [Dual - Zero Law - Pointful]
= \r -> return r
-- Eta reduce
= return
-- Goal complete
### Pointful
Goal: reflect (return r) = return r
reflect (return r)
-- Definition of `return`
= reflect (Pure r)
-- Definition of `reflect`
= Pure r
-- Definition of `return`, in reverse
= return r
-- Goal complete
## Involution
Goal: reflect . reflect = id
reflect . reflect
-- Definition of `(.)`
= \p -> reflect (reflect p)
-- [Dual - Involution]
= \p -> p
-- Definition of `id`
= id
-- Goal complete
### Pointful
reflect (reflect p) = p
reflect (reflect p)
-- Definition of `reflect`
= reflect (case p of
Request a' fa -> Respond a' (\a -> reflect (fa a ))
Respond b fb' -> Request b (\b' -> reflect (fb' b'))
M m -> M (m >>= \p' -> return (reflect p'))
Pure r -> Pure r )
-- Distribute over case statement
= case p of
Request a' fa -> reflect (Respond a' (\a -> reflect (fa a))
-- Definition of `reflect`
= Request a' (\a -> reflect (reflect (fa a)))
-- Coinduction: Reuse the premise
= Request a' (\a -> fa a)
-- Eta reduction
= Request a' fa
Respond b fb' -> reflect (Request b (\b' -> reflect (fb' b')))
-- Definition of `reflect`
= Request b (\b' -> reflect (reflect (fb' b')))
-- Coinduction: Reuse the premise
= Request b (\b' -> fb' b')
-- Eta reduction
= Request b fb'
M m -> reflect (M (m >>= \p' -> return (reflect p')))
-- Definition of `reflect`
= M (m >>= \p' -> return (reflect (reflect p')))
-- Coinduction: Reuse the premise
= M (m >>= \p' -> return p')
-- Eta reduce
= M (m >>= return)
-- [Kleisli Category - Right Identity Law - Pointful]
= M m
Pure r -> reflect (Pure r)
-- Definition of `reflect`
= Pure r
-- Clean up
= case p of
Request a' fa -> Request a' fa
Respond b fb' -> Respond b fb'
M m -> M m
Pure r -> Pure r
-- case statement = id
= p
-- Goal complete
================================================
FILE: nix/.gitkeep
================================================
================================================
FILE: pipes.cabal
================================================
Name: pipes
Version: 4.3.16
Cabal-Version: >= 1.10
Build-Type: Simple
Tested-With: GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.1
License: BSD3
License-File: LICENSE
Copyright: 2012-2016 Gabriella Gonzalez
Author: Gabriella Gonzalez
Maintainer: GenuineGabriella@gmail.com
Bug-Reports: https://github.com/Gabriella439/Haskell-Pipes-Library/issues
Synopsis: Compositional pipelines
Description:
`pipes` is a clean and powerful stream processing library that lets you build
and connect reusable streaming components
.
Advantages over traditional streaming libraries:
.
* /Concise API/: Use simple commands like 'for', ('>->'), 'await', and 'yield'
.
* /Blazing fast/: Implementation tuned for speed, including shortcut fusion
.
* /Lightweight Dependency/: @pipes@ is small and compiles very rapidly,
including dependencies
.
* /Elegant semantics/: Use practical category theory
.
* /ListT/: Correct implementation of 'ListT' that interconverts with pipes
.
* /Bidirectionality/: Implement duplex channels
.
* /Extensive Documentation/: Second to none!
.
Import "Pipes" to use the library.
.
Read "Pipes.Tutorial" for an extensive tutorial.
Category: Control, Pipes
Extra-Source-Files:
CHANGELOG.md
Source-Repository head
Type: git
Location: https://github.com/Gabriella439/Haskell-Pipes-Library
Library
Default-Language: Haskell2010
HS-Source-Dirs: src
Build-Depends:
base >= 4.8 && < 5 ,
transformers >= 0.2.0.0 && < 0.7 ,
exceptions >= 0.4 && < 0.11,
mmorph >= 1.0.4 && < 1.3 ,
mtl >= 2.2.1 && < 2.4 ,
void >= 0.4 && < 0.8
if impl(ghc < 8.0)
Build-depends:
fail == 4.9.* ,
semigroups >= 0.17 && < 0.20
Exposed-Modules:
Pipes,
Pipes.Core,
Pipes.Internal,
Pipes.Lift,
Pipes.Prelude,
Pipes.Tutorial
GHC-Options: -O2 -Wall
Benchmark prelude-benchmarks
Default-Language: Haskell2010
Type: exitcode-stdio-1.0
HS-Source-Dirs: benchmarks
Main-Is: PreludeBench.hs
Other-Modules: Common
GHC-Options: -O2 -Wall -rtsopts -fno-warn-unused-do-bind
Build-Depends:
base >= 4.4 && < 5 ,
criterion >= 1.1.1.0 && < 1.7,
optparse-applicative >= 0.12 && < 0.18,
mtl >= 2.1 && < 2.4,
pipes
test-suite tests
Default-Language: Haskell2010
Type: exitcode-stdio-1.0
HS-Source-Dirs: tests
Main-Is: Main.hs
GHC-Options: -Wall -rtsopts -fno-warn-missing-signatures -fno-enable-rewrite-rules
Build-Depends:
base >= 4.4 && < 5 ,
pipes ,
QuickCheck >= 2.4 && < 3 ,
mtl >= 2.1 && < 2.4 ,
test-framework >= 0.4 && < 1 ,
test-framework-quickcheck2 >= 0.2.0 && < 0.4 ,
transformers >= 0.2.0.0 && < 0.7
Benchmark lift-benchmarks
Default-Language: Haskell2010
Type: exitcode-stdio-1.0
HS-Source-Dirs: benchmarks
Main-Is: LiftBench.hs
Other-Modules: Common
GHC-Options: -O2 -Wall -rtsopts -fno-warn-unused-do-bind
Build-Depends:
base >= 4.4 && < 5 ,
criterion >= 1.1.1.0 && < 1.7 ,
optparse-applicative ,
mtl >= 2.1 && < 2.4 ,
pipes ,
transformers >= 0.2.0.0 && < 0.7
================================================
FILE: release.nix
================================================
let
overlay = pkgsNew: pkgsOld: {
haskellPackages = pkgsOld.haskellPackages.override (old: {
overrides =
pkgsNew.lib.fold pkgsNew.lib.composeExtensions
(old.overrides or (_: _: { }))
[ (pkgsNew.haskell.lib.packageSourceOverrides {
pipes = ./.;
})
(pkgsNew.haskell.lib.packagesFromDirectory {
directory = ./nix;
})
(haskellPackagesNew: haskellPackagesOld: {
})
];
});
};
nixpkgs = builtins.fetchTarball {
url = "https://github.com/NixOs/nixpkgs/archive/1b55bc5d4b5cb6b35d71e7fe22cae9558c312937.tar.gz";
sha256 = "05761c8bi6chlj15428h3k30r8b8g4w3h0m4xpsj6f9qcz27d9nf";
};
pkgs = import nixpkgs { config = { }; overlays = [ overlay ]; };
in
{ pipes = pkgs.haskellPackages.pipes;
}
================================================
FILE: shell.nix
================================================
(import ./release.nix).pipes.env
================================================
FILE: src/Pipes/Core.hs
================================================
{-| The core functionality for the 'Proxy' monad transformer
Read "Pipes.Tutorial" if you want a beginners tutorial explaining how to use
this library. The documentation in this module targets more advanced users
who want to understand the theory behind this library.
This module is not exported by default, and I recommend you use the
unidirectional operations exported by the "Pipes" module if you can. You
should only use this module if you require advanced features like:
* bidirectional communication, or:
* push-based 'Pipe's.
-}
{-# LANGUAGE RankNTypes, Trustworthy #-}
module Pipes.Core (
-- * Proxy Monad Transformer
-- $proxy
Proxy
, runEffect
-- * Categories
-- $categories
-- ** Respond
-- $respond
, respond
, (/>/)
, (//>)
-- ** Request
-- $request
, request
, (\>\)
, (>\\)
-- ** Push
-- $push
, push
, (>~>)
, (>>~)
-- ** Pull
-- $pull
, pull
, (>+>)
, (+>>)
-- ** Reflect
-- $reflect
, reflect
-- * Concrete Type Synonyms
, X
, Effect
, Producer
, Pipe
, Consumer
, Client
, Server
-- * Polymorphic Type Synonyms
, Effect'
, Producer'
, Consumer'
, Client'
, Server'
-- * Flipped operators
, (\<\)
, (/</)
, (<~<)
, (~<<)
, (<+<)
, (<\\)
, (//<)
, (<<+)
-- * Re-exports
, closed
) where
import Pipes.Internal (Proxy(..), X, closed)
{- $proxy
Diagrammatically, you can think of a 'Proxy' as having the following shape:
@
Upstream | Downstream
+---------+
| |
a' <== <== b'
| |
a ==> ==> b
| | |
+----|----+
v
r
@
You can connect proxies together in five different ways:
* ('Pipes.>+>'): connect pull-based streams
* ('Pipes.>~>'): connect push-based streams
* ('Pipes.\>\'): chain folds
* ('Pipes./>/'): chain unfolds
* ('Control.Monad.>=>'): sequence proxies
-}
-- | Run a self-contained 'Effect', converting it back to the base monad
runEffect :: Monad m => Effect m r -> m r
runEffect = go
where
go p = case p of
Request v _ -> closed v
Respond v _ -> closed v
M m -> m >>= go
Pure r -> return r
{-# INLINABLE runEffect #-}
{- * Keep proxy composition lower in precedence than function composition, which
is 9 at the time of of this comment, so that users can write things like:
> lift . k >+> p
>
> hoist f . k >+> p
* Keep the priorities different so that users can mix composition operators
like:
> up \>\ p />/ dn
>
> up >~> p >+> dn
* Keep 'request' and 'respond' composition lower in precedence than 'pull'
and 'push' composition, so that users can do:
> read \>\ pull >+> writer
* I arbitrarily choose a lower priority for downstream operators so that lazy
pull-based computations need not evaluate upstream stages unless absolutely
necessary.
-}
infixl 3 //>
infixr 3 <\\ -- GHC will raise a parse error if either of these lines ends
infixr 4 />/, >\\ -- with '\', which is why this comment is here
infixl 4 \<\, //<
infixl 5 \>\ -- Same thing here
infixr 5 /</
infixl 6 <<+
infixr 6 +>>
infixl 7 >+>, >>~
infixr 7 <+<, ~<<
infixl 8 <~<
infixr 8 >~>
{- $categories
A 'Control.Category.Category' is a set of components that you can connect
with a composition operator, ('Control.Category..'), that has an identity,
'Control.Category.id'. The ('Control.Category..') and 'Control.Category.id'
must satisfy the following three 'Control.Category.Category' laws:
@
\-\- Left identity
'Control.Category.id' 'Control.Category..' f = f
\-\- Right identity
f 'Control.Category..' 'Control.Category.id' = f
\-\- Associativity
(f 'Control.Category..' g) 'Control.Category..' h = f 'Control.Category..' (g 'Control.Category..' h)
@
The 'Proxy' type sits at the intersection of five separate categories, four
of which are named after their identity:
@
Identity | Composition | Point-ful
+-------------+-------------+-------------+
respond category | 'respond' | '/>/' | '//>' |
request category | 'request' | '\>\' | '>\\' |
push category | 'push' | '>~>' | '>>~' |
pull category | 'pull' | '>+>' | '+>>' |
Kleisli category | 'return' | 'Control.Monad.>=>' | '>>=' |
+-------------+-------------+-------------+
@
Each composition operator has a \"point-ful\" version, analogous to how
('>>=') is the point-ful version of ('Control.Monad.>=>'). For example,
('//>') is the point-ful version of ('/>/'). The convention is that the
odd character out faces the argument that is a function.
-}
{- $respond
The 'respond' category closely corresponds to the generator design pattern.
The 'respond' category obeys the category laws, where 'respond' is the
identity and ('/>/') is composition:
@
\-\- Left identity
'respond' '/>/' f = f
\-\- Right identity
f '/>/' 'respond' = f
\-\- Associativity
(f '/>/' g) '/>/' h = f '/>/' (g '/>/' h)
@
#respond-diagram#
The following diagrams show the flow of information:
@
'respond' :: 'Functor' m
=> a -> 'Proxy' x' x a' a m a'
\ a
|
+----|----+
| | |
x' <== \\ /==== a'
| X |
x ==> / \\===> a
| | |
+----|----+
v
a'
('/>/') :: 'Functor' m
=> (a -> 'Proxy' x' x b' b m a')
-> (b -> 'Proxy' x' x c' c m b')
-> (a -> 'Proxy' x' x c' c m a')
\ a /===> b a
| / | |
+----|----+ / +----|----+ +----|----+
| v | / | v | | v |
x' <== <== b' <==\\ / x'<== <== c' x' <== <== c'
| f | X | g | = | f '/>/' g |
x ==> ==> b ===/ \\ x ==> ==> c x ==> ==> c
| | | \\ | | | | | |
+----|----+ \\ +----|----+ +----|----+
v \\ v v
a' \\==== b' a'
('//>') :: 'Functor' m
=> 'Proxy' x' x b' b m a'
-> (b -> 'Proxy' x' x c' c m b')
-> 'Proxy' x' x c' c m a'
\ /===> b
/ |
+---------+ / +----|----+ +---------+
| | / | v | | |
x' <== <== b' <==\\ / x'<== <== c' x' <== <== c'
| f | X | g | = | f '//>' g |
x ==> ==> b ===/ \\ x ==> ==> c x ==> ==> c'
| | | \\ | | | | | |
+----|----+ \\ +----|----+ +----|----+
v \\ v v
a' \\==== b' a'
@
-}
{-| Send a value of type @a@ downstream and block waiting for a reply of type
@a'@
'respond' is the identity of the respond category.
-}
respond :: Functor m => a -> Proxy x' x a' a m a'
respond a = Respond a Pure
{-# INLINABLE [1] respond #-}
{-| Compose two unfolds, creating a new unfold
@
(f '/>/' g) x = f x '//>' g
@
('/>/') is the composition operator of the respond category.
-}
(/>/)
:: Functor m
=> (a -> Proxy x' x b' b m a')
-- ^
-> (b -> Proxy x' x c' c m b')
-- ^
-> (a -> Proxy x' x c' c m a')
-- ^
(fa />/ fb) a = fa a //> fb
{-# INLINABLE (/>/) #-}
{-| @(p \/\/> f)@ replaces each 'respond' in @p@ with @f@.
Point-ful version of ('/>/')
-}
(//>)
:: Functor m
=> Proxy x' x b' b m a'
-- ^
-> (b -> Proxy x' x c' c m b')
-- ^
-> Proxy x' x c' c m a'
-- ^
p0 //> fb = go p0
where
go p = case p of
Request x' fx -> Request x' (\x -> go (fx x))
Respond b fb' -> fb b >>= \b' -> go (fb' b')
M m -> M (go <$> m)
Pure a -> Pure a
{-# INLINE [1] (//>) #-}
{-# RULES
"(Request x' fx ) //> fb" forall x' fx fb .
(Request x' fx ) //> fb = Request x' (\x -> fx x //> fb);
"(Respond b fb') //> fb" forall b fb' fb .
(Respond b fb') //> fb = fb b >>= \b' -> fb' b' //> fb;
"(M m ) //> fb" forall m fb .
(M m ) //> fb = M ((\p' -> p' //> fb) <$> m);
"(Pure a ) //> fb" forall a fb .
(Pure a ) //> fb = Pure a;
#-}
{- $request
The 'request' category closely corresponds to the iteratee design pattern.
The 'request' category obeys the category laws, where 'request' is the
identity and ('\>\') is composition:
@
-- Left identity
'request' '\>\' f = f
\-\- Right identity
f '\>\' 'request' = f
\-\- Associativity
(f '\>\' g) '\>\' h = f '\>\' (g '\>\' h)
@
#request-diagram#
The following diagrams show the flow of information:
@
'request' :: 'Functor' m
=> a' -> 'Proxy' a' a y' y m a
\ a'
|
+----|----+
| | |
a' <=====/ <== y'
| |
a ======\\ ==> y
| | |
+----|----+
v
a
('\>\') :: 'Functor' m
=> (b' -> 'Proxy' a' a y' y m b)
-> (c' -> 'Proxy' b' b y' y m c)
-> (c' -> 'Proxy' a' a y' y m c)
\ b'<=====\\ c' c'
| \\ | |
+----|----+ \\ +----|----+ +----|----+
| v | \\ | v | | v |
a' <== <== y' \\== b' <== <== y' a' <== <== y'
| f | | g | = | f '\>\' g |
a ==> ==> y /=> b ==> ==> y a ==> ==> y
| | | / | | | | | |
+----|----+ / +----|----+ +----|----+
v / v v
b ======/ c c
('>\\') :: Functor m
=> (b' -> Proxy a' a y' y m b)
-> Proxy b' b y' y m c
-> Proxy a' a y' y m c
\ b'<=====\\
| \\
+----|----+ \\ +---------+ +---------+
| v | \\ | | | |
a' <== <== y' \\== b' <== <== y' a' <== <== y'
| f | | g | = | f '>\\' g |
a ==> ==> y /=> b ==> ==> y a ==> ==> y
| | | / | | | | | |
+----|----+ / +----|----+ +----|----+
v / v v
b ======/ c c
@
-}
{-| Send a value of type @a'@ upstream and block waiting for a reply of type @a@
'request' is the identity of the request category.
-}
request :: Functor m => a' -> Proxy a' a y' y m a
request a' = Request a' Pure
{-# INLINABLE [1] request #-}
{-| Compose two folds, creating a new fold
@
(f '\>\' g) x = f '>\\' g x
@
('\>\') is the composition operator of the request category.
-}
(\>\)
:: Functor m
=> (b' -> Proxy a' a y' y m b)
-- ^
-> (c' -> Proxy b' b y' y m c)
-- ^
-> (c' -> Proxy a' a y' y m c)
-- ^
(fb' \>\ fc') c' = fb' >\\ fc' c'
{-# INLINABLE (\>\) #-}
{-| @(f >\\\\ p)@ replaces each 'request' in @p@ with @f@.
Point-ful version of ('\>\')
-}
(>\\)
:: Functor m
=> (b' -> Proxy a' a y' y m b)
-- ^
-> Proxy b' b y' y m c
-- ^
-> Proxy a' a y' y m c
-- ^
fb' >\\ p0 = go p0
where
go p = case p of
Request b' fb -> fb' b' >>= \b -> go (fb b)
Respond x fx' -> Respond x (\x' -> go (fx' x'))
M m -> M (go <$> m)
Pure a -> Pure a
{-# INLINE [1] (>\\) #-}
{-# RULES
"fb' >\\ (Request b' fb )" forall fb' b' fb .
fb' >\\ (Request b' fb ) = fb' b' >>= \b -> fb' >\\ fb b;
"fb' >\\ (Respond x fx')" forall fb' x fx' .
fb' >\\ (Respond x fx') = Respond x (\x' -> fb' >\\ fx' x');
"fb' >\\ (M m )" forall fb' m .
fb' >\\ (M m ) = M ((\p' -> fb' >\\ p') <$> m);
"fb' >\\ (Pure a )" forall fb' a .
fb' >\\ (Pure a ) = Pure a;
#-}
{- $push
The 'push' category closely corresponds to push-based Unix pipes.
The 'push' category obeys the category laws, where 'push' is the identity
and ('>~>') is composition:
@
\-\- Left identity
'push' '>~>' f = f
\-\- Right identity
f '>~>' 'push' = f
\-\- Associativity
(f '>~>' g) '>~>' h = f '>~>' (g '>~>' h)
@
The following diagram shows the flow of information:
@
'push' :: 'Functor' m
=> a -> 'Proxy' a' a a' a m r
\ a
|
+----|----+
| v |
a' <============ a'
| |
a ============> a
| | |
+----|----+
v
r
('>~>') :: 'Functor' m
=> (a -> 'Proxy' a' a b' b m r)
-> (b -> 'Proxy' b' b c' c m r)
-> (a -> 'Proxy' a' a c' c m r)
\ a b a
| | |
+----|----+ +----|----+ +----|----+
| v | | v | | v |
a' <== <== b' <== <== c' a' <== <== c'
| f | | g | = | f '>~>' g |
a ==> ==> b ==> ==> c a ==> ==> c
| | | | | | | | |
+----|----+ +----|----+ +----|----+
v v v
r r r
@
-}
{-| Forward responses followed by requests
@
'push' = 'respond' 'Control.Monad.>=>' 'request' 'Control.Monad.>=>' 'push'
@
'push' is the identity of the push category.
-}
push :: Functor m => a -> Proxy a' a a' a m r
push = go
where
go a = Respond a (\a' -> Request a' go)
{-# INLINABLE [1] push #-}
{-| Compose two proxies blocked while 'request'ing data, creating a new proxy
blocked while 'request'ing data
@
(f '>~>' g) x = f x '>>~' g
@
('>~>') is the composition operator of the push category.
-}
(>~>)
:: Functor m
=> (_a -> Proxy a' a b' b m r)
-- ^
-> ( b -> Proxy b' b c' c m r)
-- ^
-> (_a -> Proxy a' a c' c m r)
-- ^
(fa >~> fb) a = fa a >>~ fb
{-# INLINABLE (>~>) #-}
{-| @(p >>~ f)@ pairs each 'respond' in @p@ with a 'request' in @f@.
Point-ful version of ('>~>')
-}
(>>~)
:: Functor m
=> Proxy a' a b' b m r
-- ^
-> (b -> Proxy b' b c' c m r)
-- ^
-> Proxy a' a c' c m r
-- ^
p >>~ fb = case p of
Request a' fa -> Request a' (\a -> fa a >>~ fb)
Respond b fb' -> fb' +>> fb b
M m -> M ((\p' -> p' >>~ fb) <$> m)
Pure r -> Pure r
{-# INLINE [1] (>>~) #-}
{- $pull
The 'pull' category closely corresponds to pull-based Unix pipes.
The 'pull' category obeys the category laws, where 'pull' is the identity
and ('>+>') is composition:
@
\-\- Left identity
'pull' '>+>' f = f
\-\- Right identity
f '>+>' 'pull' = f
\-\- Associativity
(f '>+>' g) '>+>' h = f '>+>' (g '>+>' h)
@
#pull-diagram#
The following diagrams show the flow of information:
@
'pull' :: 'Functor' m
=> a' -> 'Proxy' a' a a' a m r
\ a'
|
+----|----+
| v |
a' <============ a'
| |
a ============> a
| | |
+----|----+
v
r
('>+>') :: 'Functor' m
-> (b' -> 'Proxy' a' a b' b m r)
-> (c' -> 'Proxy' b' b c' c m r)
-> (c' -> 'Proxy' a' a c' c m r)
\ b' c' c'
| | |
+----|----+ +----|----+ +----|----+
| v | | v | | v |
a' <== <== b' <== <== c' a' <== <== c'
| f | | g | = | f >+> g |
a ==> ==> b ==> ==> c a ==> ==> c
| | | | | | | | |
+----|----+ +----|----+ +----|----+
v v v
r r r
@
-}
{-| Forward requests followed by responses:
@
'pull' = 'request' 'Control.Monad.>=>' 'respond' 'Control.Monad.>=>' 'pull'
@
'pull' is the identity of the pull category.
-}
pull :: Functor m => a' -> Proxy a' a a' a m r
pull = go
where
go a' = Request a' (\a -> Respond a go)
{-# INLINABLE [1] pull #-}
{-| Compose two proxies blocked in the middle of 'respond'ing, creating a new
proxy blocked in the middle of 'respond'ing
@
(f '>+>' g) x = f '+>>' g x
@
('>+>') is the composition operator of the pull category.
-}
(>+>)
:: Functor m
=> ( b' -> Proxy a' a b' b m r)
-- ^
-> (_c' -> Proxy b' b c' c m r)
-- ^
-> (_c' -> Proxy a' a c' c m r)
-- ^
(fb' >+> fc') c' = fb' +>> fc' c'
{-# INLINABLE (>+>) #-}
{-| @(f +>> p)@ pairs each 'request' in @p@ with a 'respond' in @f@.
Point-ful version of ('>+>')
-}
(+>>)
:: Functor m
=> (b' -> Proxy a' a b' b m r)
-- ^
-> Proxy b' b c' c m r
-- ^
-> Proxy a' a c' c m r
-- ^
fb' +>> p = case p of
Request b' fb -> fb' b' >>~ fb
Respond c fc' -> Respond c (\c' -> fb' +>> fc' c')
M m -> M ((\p' -> fb' +>> p') <$> m)
Pure r -> Pure r
{-# INLINABLE [1] (+>>) #-}
{- $reflect
@(reflect .)@ transforms each streaming category into its dual:
* The request category is the dual of the respond category
@
'reflect' '.' 'respond' = 'request'
'reflect' '.' (f '/>/' g) = 'reflect' '.' f '/</' 'reflect' '.' g
@
@
'reflect' '.' 'request' = 'respond'
'reflect' '.' (f '\>\' g) = 'reflect' '.' f '\<\' 'reflect' '.' g
@
* The pull category is the dual of the push category
@
'reflect' '.' 'push' = 'pull'
'reflect' '.' (f '>~>' g) = 'reflect' '.' f '<+<' 'reflect' '.' g
@
@
'reflect' '.' 'pull' = 'push'
'reflect' '.' (f '>+>' g) = 'reflect' '.' f '<~<' 'reflect' '.' g
@
-}
-- | Switch the upstream and downstream ends
reflect :: Functor m => Proxy a' a b' b m r -> Proxy b b' a a' m r
reflect = go
where
go p = case p of
Request a' fa -> Respond a' (\a -> go (fa a ))
Respond b fb' -> Request b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure r -> Pure r
{-# INLINABLE reflect #-}
{-| An effect in the base monad
'Effect's neither 'Pipes.await' nor 'Pipes.yield'
-}
type Effect = Proxy X () () X
-- | 'Producer's can only 'Pipes.yield'
type Producer b = Proxy X () () b
-- | 'Pipe's can both 'Pipes.await' and 'Pipes.yield'
type Pipe a b = Proxy () a () b
-- | 'Consumer's can only 'Pipes.await'
type Consumer a = Proxy () a () X
{-| @Client a' a@ sends requests of type @a'@ and receives responses of
type @a@.
'Client's only 'request' and never 'respond'.
-}
type Client a' a = Proxy a' a () X
{-| @Server b' b@ receives requests of type @b'@ and sends responses of type
@b@.
'Server's only 'respond' and never 'request'.
-}
type Server b' b = Proxy X () b' b
-- | Like 'Effect', but with a polymorphic type
type Effect' m r = forall x' x y' y . Proxy x' x y' y m r
-- | Like 'Producer', but with a polymorphic type
type Producer' b m r = forall x' x . Proxy x' x () b m r
-- | Like 'Consumer', but with a polymorphic type
type Consumer' a m r = forall y' y . Proxy () a y' y m r
-- | Like 'Server', but with a polymorphic type
type Server' b' b m r = forall x' x . Proxy x' x b' b m r
-- | Like 'Client', but with a polymorphic type
type Client' a' a m r = forall y' y . Proxy a' a y' y m r
-- | Equivalent to ('/>/') with the arguments flipped
(\<\)
:: Functor m
=> (b -> Proxy x' x c' c m b')
-- ^
-> (a -> Proxy x' x b' b m a')
-- ^
-> (a -> Proxy x' x c' c m a')
-- ^
p1 \<\ p2 = p2 />/ p1
{-# INLINABLE (\<\) #-}
-- | Equivalent to ('\>\') with the arguments flipped
(/</)
:: Functor m
=> (c' -> Proxy b' b x' x m c)
-- ^
-> (b' -> Proxy a' a x' x m b)
-- ^
-> (c' -> Proxy a' a x' x m c)
-- ^
p1 /</ p2 = p2 \>\ p1
{-# INLINABLE (/</) #-}
-- | Equivalent to ('>~>') with the arguments flipped
(<~<)
:: Functor m
=> (b -> Proxy b' b c' c m r)
-- ^
-> (a -> Proxy a' a b' b m r)
-- ^
-> (a -> Proxy a' a c' c m r)
-- ^
p1 <~< p2 = p2 >~> p1
{-# INLINABLE (<~<) #-}
-- | Equivalent to ('>+>') with the arguments flipped
(<+<)
:: Functor m
=> (c' -> Proxy b' b c' c m r)
-- ^
-> (b' -> Proxy a' a b' b m r)
-- ^
-> (c' -> Proxy a' a c' c m r)
-- ^
p1 <+< p2 = p2 >+> p1
{-# INLINABLE (<+<) #-}
-- | Equivalent to ('//>') with the arguments flipped
(<\\)
:: Functor m
=> (b -> Proxy x' x c' c m b')
-- ^
-> Proxy x' x b' b m a'
-- ^
-> Proxy x' x c' c m a'
-- ^
f <\\ p = p //> f
{-# INLINABLE (<\\) #-}
-- | Equivalent to ('>\\') with the arguments flipped
(//<)
:: Functor m
=> Proxy b' b y' y m c
-- ^
-> (b' -> Proxy a' a y' y m b)
-- ^
-> Proxy a' a y' y m c
-- ^
p //< f = f >\\ p
{-# INLINABLE (//<) #-}
-- | Equivalent to ('>>~') with the arguments flipped
(~<<)
:: Functor m
=> (b -> Proxy b' b c' c m r)
-- ^
-> Proxy a' a b' b m r
-- ^
-> Proxy a' a c' c m r
-- ^
k ~<< p = p >>~ k
{-# INLINABLE (~<<) #-}
-- | Equivalent to ('+>>') with the arguments flipped
(<<+)
:: Functor m
=> Proxy b' b c' c m r
-- ^
-> (b' -> Proxy a' a b' b m r)
-- ^
-> Proxy a' a c' c m r
-- ^
k <<+ p = p +>> k
{-# INLINABLE (<<+) #-}
{-# RULES
"(p //> f) //> g" forall p f g . (p //> f) //> g = p //> (\x -> f x //> g)
; "p //> respond" forall p . p //> respond = p
; "respond x //> f" forall x f . respond x //> f = f x
; "f >\\ (g >\\ p)" forall f g p . f >\\ (g >\\ p) = (\x -> f >\\ g x) >\\ p
; "request >\\ p" forall p . request >\\ p = p
; "f >\\ request x" forall f x . f >\\ request x = f x
; "(p >>~ f) >>~ g" forall p f g . (p >>~ f) >>~ g = p >>~ (\x -> f x >>~ g)
; "p >>~ push" forall p . p >>~ push = p
; "push x >>~ f" forall x f . push x >>~ f = f x
; "f +>> (g +>> p)" forall f g p . f +>> (g +>> p) = (\x -> f +>> g x) +>> p
; "pull +>> p" forall p . pull +>> p = p
; "f +>> pull x" forall f x . f +>> pull x = f x
#-}
================================================
FILE: src/Pipes/Internal.hs
================================================
{-| This is an internal module, meaning that it is unsafe to import unless you
understand the risks.
This module provides a fast implementation by weakening the monad
transformer laws. These laws do not hold if you can pattern match on the
constructors, as the following counter-example illustrates:
@
'lift' '.' 'return' = 'M' '.' 'return' '.' 'Pure'
'return' = 'Pure'
'lift' '.' 'return' /= 'return'
@
You do not need to worry about this if you do not import this module, since
the other modules in this library do not export the constructors or export
any functions which can violate the monad transformer laws.
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE Trustworthy #-}
module Pipes.Internal (
-- * Internal
Proxy(..)
, unsafeHoist
, observe
, X
, closed
) where
import qualified Control.Monad.Fail as F (MonadFail(fail))
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Trans.Class (MonadTrans(lift))
import Control.Monad.Morph (MFunctor(hoist), MMonad(embed))
import Control.Monad.Except (MonadError(..))
import Control.Monad.Catch (MonadThrow(..), MonadCatch(..))
import Control.Monad.Reader (MonadReader(..))
import Control.Monad.State (MonadState(..))
import Control.Monad.Writer (MonadWriter(..), censor)
import Data.Void (Void)
#if MIN_VERSION_base(4,8,0)
import Control.Applicative (Alternative(..))
#else
import Control.Applicative
#endif
import Data.Semigroup
import qualified Data.Void
{-| A 'Proxy' is a monad transformer that receives and sends information on both
an upstream and downstream interface.
The type variables signify:
* @a'@ and @a@ - The upstream interface, where @(a')@s go out and @(a)@s
come in
* @b'@ and @b@ - The downstream interface, where @(b)@s go out and @(b')@s
come in
* @m @ - The base monad
* @r @ - The return value
-}
data Proxy a' a b' b m r
= Request a' (a -> Proxy a' a b' b m r )
| Respond b (b' -> Proxy a' a b' b m r )
| M (m (Proxy a' a b' b m r))
| Pure r
instance Functor m => Functor (Proxy a' a b' b m) where
fmap f p0 = go p0 where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure r -> Pure (f r)
instance Functor m => Applicative (Proxy a' a b' b m) where
pure = Pure
pf <*> px = go pf where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure f -> fmap f px
l *> r = go l where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure _ -> r
instance Functor m => Monad (Proxy a' a b' b m) where
return = pure
(>>=) = _bind
_bind
:: Functor m
=> Proxy a' a b' b m r
-> (r -> Proxy a' a b' b m r')
-> Proxy a' a b' b m r'
p0 `_bind` f = go p0 where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure r -> f r
{-# NOINLINE[1] _bind #-}
{-# RULES
"_bind (Request a' k) f" forall a' k f .
_bind (Request a' k) f = Request a' (\a -> _bind (k a) f);
"_bind (Respond b k) f" forall b k f .
_bind (Respond b k) f = Respond b (\b' -> _bind (k b') f);
"_bind (M m) f" forall m f .
_bind (M m) f = M ((\p -> _bind p f) <$> m);
"_bind (Pure r ) f" forall r f .
_bind (Pure r ) f = f r;
#-}
instance (Functor m, Semigroup r) => Semigroup (Proxy a' a b' b m r) where
p1 <> p2 = go p1 where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (go <$> m)
Pure r1 -> fmap (r1 <>) p2
instance (Functor m, Monoid r, Semigroup r) => Monoid (Proxy a' a b' b m r) where
mempty = Pure mempty
#if !(MIN_VERSION_base(4,11,0))
mappend = (<>)
#endif
instance MonadTrans (Proxy a' a b' b) where
lift m = M (Pure <$> m)
{-| 'unsafeHoist' is like 'hoist', but faster.
This is labeled as unsafe because you will break the monad transformer laws
if you do not pass a monad morphism as the first argument. This function is
safe if you pass a monad morphism as the first argument.
-}
unsafeHoist
:: Functor m
=> (forall x . m x -> n x) -> Proxy a' a b' b m r -> Proxy a' a b' b n r
unsafeHoist nat = go
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (nat (go <$> m))
Pure r -> Pure r
{-# INLINABLE unsafeHoist #-}
instance MFunctor (Proxy a' a b' b) where
hoist nat p0 = go (observe p0)
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> M (nat (go <$> m))
Pure r -> Pure r
instance MMonad (Proxy a' a b' b) where
embed f = go
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
M m -> f m >>= go
Pure r -> Pure r
instance F.MonadFail m => F.MonadFail (Proxy a' a b' b m) where
fail = lift . F.fail
instance MonadIO m => MonadIO (Proxy a' a b' b m) where
liftIO m = M (liftIO (Pure <$> m))
instance MonadReader r m => MonadReader r (Proxy a' a b' b m) where
ask = lift ask
local f = go
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
Pure r -> Pure r
M m -> M (go <$> local f m)
reader = lift . reader
instance MonadState s m => MonadState s (Proxy a' a b' b m) where
get = lift get
put = lift . put
state = lift . state
instance MonadWriter w m => MonadWriter w (Proxy a' a b' b m) where
writer = lift . writer
tell = lift . tell
listen p0 = go p0 mempty
where
go p w = case p of
Request a' fa -> Request a' (\a -> go (fa a ) w)
Respond b fb' -> Respond b (\b' -> go (fb' b') w)
M m -> M (do
(p', w') <- listen m
return (go p' $! mappend w w') )
Pure r -> Pure (r, w)
pass p0 = go p0 mempty
where
go p w = case p of
Request a' fa -> Request a' (\a -> go (fa a ) w)
Respond b fb' -> Respond b (\b' -> go (fb' b') w)
M m -> M (do
(p', w') <- censor (const mempty) (listen m)
return (go p' $! mappend w w') )
Pure (r, f) -> M (pass (return (Pure r, \_ -> f w)))
instance MonadError e m => MonadError e (Proxy a' a b' b m) where
throwError = lift . throwError
catchError p0 f = go p0
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
Pure r -> Pure r
M m -> M ((do
p' <- m
return (go p') ) `catchError` (\e -> return (f e)) )
instance MonadThrow m => MonadThrow (Proxy a' a b' b m) where
throwM = lift . throwM
{-# INLINE throwM #-}
instance MonadCatch m => MonadCatch (Proxy a' a b' b m) where
catch p0 f = go p0
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
Pure r -> Pure r
M m -> M ((do
p' <- m
return (go p') ) `Control.Monad.Catch.catch` (\e -> return (f e)) )
{-| The monad transformer laws are correct when viewed through the 'observe'
function:
@
'observe' ('lift' ('return' r)) = 'observe' ('return' r)
'observe' ('lift' (m '>>=' f)) = 'observe' ('lift' m '>>=' 'lift' '.' f)
@
This correctness comes at a small cost to performance, so use this function
sparingly.
This function is a convenience for low-level @pipes@ implementers. You do
not need to use 'observe' if you stick to the safe API.
-}
observe :: Monad m => Proxy a' a b' b m r -> Proxy a' a b' b m r
observe p0 = M (go p0) where
go p = case p of
Request a' fa -> return (Request a' (\a -> observe (fa a )))
Respond b fb' -> return (Respond b (\b' -> observe (fb' b')))
M m' -> m' >>= go
Pure r -> return (Pure r)
{-# INLINABLE observe #-}
-- | The empty type, used to close output ends
type X = Void
-- | Use 'closed' to \"handle\" impossible outputs
closed :: X -> a
closed = Data.Void.absurd
{-# INLINABLE closed #-}
================================================
FILE: src/Pipes/Lift.hs
================================================
{-# LANGUAGE CPP #-}
{-| Many actions in base monad transformers cannot be automatically
'Control.Monad.Trans.Class.lift'ed. These functions lift these remaining
actions so that they work in the 'Proxy' monad transformer.
See the mini-tutorial at the bottom of this module for example code and
typical use cases where this module will come in handy.
-}
module Pipes.Lift (
-- * Utilities
distribute
-- * ExceptT
, exceptP
, runExceptP
, catchError
, liftCatchError
-- * MaybeT
, maybeP
, runMaybeP
-- * ReaderT
, readerP
, runReaderP
-- * StateT
, stateP
, runStateP
, evalStateP
, execStateP
-- * WriterT
-- $writert
, writerP
, runWriterP
, execWriterP
-- * RWST
, rwsP
, runRWSP
, evalRWSP
, execRWSP
-- * Tutorial
-- $tutorial
) where
import Control.Monad.Trans.Class (lift, MonadTrans(..))
import qualified Control.Monad.Trans.Except as E
import qualified Control.Monad.Trans.Maybe as M
import qualified Control.Monad.Trans.Reader as R
import qualified Control.Monad.Trans.State.Strict as S
import qualified Control.Monad.Trans.Writer.Strict as W
import qualified Control.Monad.Trans.RWS.Strict as RWS
import Pipes.Internal (Proxy(..), unsafeHoist)
import Control.Monad.Morph (hoist, MFunctor(..))
import Pipes.Core (runEffect, request, respond, (//>), (>\\))
#if MIN_VERSION_base(4,8,0)
#else
import Data.Monoid
#endif
-- | Distribute 'Proxy' over a monad transformer
distribute
:: ( Monad m
, MonadTrans t
, MFunctor t
, Monad (t m)
, Monad (t (Proxy a' a b' b m))
)
=> Proxy a' a b' b (t m) r
-- ^
-> t (Proxy a' a b' b m) r
-- ^
distribute p = runEffect $ request' >\\ unsafeHoist (hoist lift) p //> respond'
where
request' = lift . lift . request
respond' = lift . lift . respond
{-# INLINABLE distribute #-}
-- | Wrap the base monad in 'E.ExceptT'
exceptP
:: Monad m
=> Proxy a' a b' b m (Either e r)
-> Proxy a' a b' b (E.ExceptT e m) r
exceptP p = do
x <- unsafeHoist lift p
lift $ E.ExceptT (return x)
{-# INLINABLE exceptP #-}
-- | Run 'E.ExceptT' in the base monad
runExceptP
:: Monad m
=> Proxy a' a b' b (E.ExceptT e m) r
-> Proxy a' a b' b m (Either e r)
runExceptP = E.runExceptT . distribute
{-# INLINABLE runExceptP #-}
-- | Catch an error in the base monad
catchError
:: Monad m
=> Proxy a' a b' b (E.ExceptT e m) r
-- ^
-> (e -> Proxy a' a b' b (E.ExceptT e m) r)
-- ^
-> Proxy a' a b' b (E.ExceptT e m) r
catchError e h = exceptP . E.runExceptT $
E.catchE (distribute e) (distribute . h)
{-# INLINABLE catchError #-}
-- | Catch an error using a catch function for the base monad
liftCatchError
:: Monad m
=> ( m (Proxy a' a b' b m r)
-> (e -> m (Proxy a' a b' b m r))
-> m (Proxy a' a b' b m r) )
-- ^
-> (Proxy a' a b' b m r
-> (e -> Proxy a' a b' b m r)
-> Proxy a' a b' b m r)
-- ^
liftCatchError c p0 f = go p0
where
go p = case p of
Request a' fa -> Request a' (\a -> go (fa a ))
Respond b fb' -> Respond b (\b' -> go (fb' b'))
Pure r -> Pure r
M m -> M ((do
p' <- m
return (go p') ) `c` (\e -> return (f e)) )
{-# INLINABLE liftCatchError #-}
-- | Wrap the base monad in 'M.MaybeT'
maybeP
:: Monad m
=> Proxy a' a b' b m (Maybe r) -> Proxy a' a b' b (M.MaybeT m) r
maybeP p = do
x <- unsafeHoist lift p
lift $ M.MaybeT (return x)
{-# INLINABLE maybeP #-}
-- | Run 'M.MaybeT' in the base monad
runMaybeP
:: Monad m
=> Proxy a' a b' b (M.MaybeT m) r
-> Proxy a' a b' b m (Maybe r)
runMaybeP p = M.runMaybeT $ distribute p
{-# INLINABLE runMaybeP #-}
-- | Wrap the base monad in 'R.ReaderT'
readerP
:: Monad m
=> (i -> Proxy a' a b' b m r) -> Proxy a' a b' b (R.ReaderT i m) r
readerP k = do
i <- lift R.ask
unsafeHoist lift (k i)
{-# INLINABLE readerP #-}
-- | Run 'R.ReaderT' in the base monad
runReaderP
:: Monad m
=> i
-> Proxy a' a b' b (R.ReaderT i m) r
-> Proxy a' a b' b m r
runReaderP r p = (`R.runReaderT` r) $ distribute p
{-# INLINABLE runReaderP #-}
-- | Wrap the base monad in 'S.StateT'
stateP
:: Monad m
=> (s -> Proxy a' a b' b m (r, s)) -> Proxy a' a b' b (S.StateT s m) r
stateP k = do
s <- lift S.get
(r, s') <- unsafeHoist lift (k s)
lift (S.put s')
return r
{-# INLINABLE stateP #-}
-- | Run 'S.StateT' in the base monad
runStateP
:: Monad m
=> s
-> Proxy a' a b' b (S.StateT s m) r
-> Proxy a' a b' b m (r, s)
runStateP s p = (`S.runStateT` s) $ distribute p
{-# INLINABLE runStateP #-}
-- | Evaluate 'S.StateT' in the base monad
evalStateP
:: Monad m
=> s
-> Proxy a' a b' b (S.StateT s m) r
-> Proxy a' a b' b m r
evalStateP s p = fmap fst $ runStateP s p
{-# INLINABLE evalStateP #-}
-- | Execute 'S.StateT' in the base monad
execStateP
:: Monad m
=> s
-> Proxy a' a b' b (S.StateT s m) r
-> Proxy a' a b' b m s
execStateP s p = fmap snd $ runStateP s p
{-# INLINABLE execStateP #-}
{- $writert
Note that 'runWriterP' and 'execWriterP' will keep the accumulator in
weak-head-normal form so that folds run in constant space when possible.
This means that until @transformers@ adds a truly strict 'W.WriterT', you
should consider unwrapping 'W.WriterT' first using 'runWriterP' or
'execWriterP' before running your 'Proxy'. You will get better performance
this way and eliminate space leaks if your accumulator doesn't have any lazy
fields.
-}
-- | Wrap the base monad in 'W.WriterT'
writerP
:: (Monad m, Monoid w)
=> Proxy a' a b' b m (r, w) -> Proxy a' a b' b (W.WriterT w m) r
writerP p = do
(r, w) <- unsafeHoist lift p
lift $ W.tell w
return r
{-# INLINABLE writerP #-}
-- | Run 'W.WriterT' in the base monad
runWriterP
:: (Monad m, Monoid w)
=> Proxy a' a b' b (W.WriterT w m) r
-> Proxy a' a b' b m (r, w)
runWriterP p = W.runWriterT $ distribute p
{-# INLINABLE runWriterP #-}
-- | Execute 'W.WriterT' in the base monad
execWriterP
:: (Monad m, Monoid w)
=> Proxy a' a b' b (W.WriterT w m) r
-> Proxy a' a b' b m w
execWriterP p = fmap snd $ runWriterP p
{-# INLINABLE execWriterP #-}
-- | Wrap the base monad in 'RWS.RWST'
rwsP
:: (Monad m, Monoid w)
=> (i -> s -> Proxy a' a b' b m (r, s, w))
-> Proxy a' a b' b (RWS.RWST i w s m) r
rwsP k = do
i <- lift RWS.ask
s <- lift RWS.get
(r, s', w) <- unsafeHoist lift (k i s)
lift $ do
RWS.put s'
RWS.tell w
return r
{-# INLINABLE rwsP #-}
-- | Run 'RWS.RWST' in the base monad
runRWSP
:: (Monad m, Monoid w)
=> r
-> s
-> Proxy a' a b' b (RWS.RWST r w s m) d
-> Proxy a' a b' b m (d, s, w)
runRWSP i s p = (\b -> RWS.runRWST b i s) $ distribute p
{-# INLINABLE runRWSP #-}
-- | Evaluate 'RWS.RWST' in the base monad
evalRWSP
:: (Monad m, Monoid w)
=> r
-> s
-> Proxy a' a b' b (RWS.RWST r w s m) d
-> Proxy a' a b' b m (d, w)
evalRWSP i s p = fmap f $ runRWSP i s p
where
f x = let (r, _, w) = x in (r, w)
{-# INLINABLE evalRWSP #-}
-- | Execute 'RWS.RWST' in the base monad
execRWSP
:: (Monad m, Monoid w)
=> r
-> s
-> Proxy a' a b' b (RWS.RWST r w s m) d
-> Proxy a' a b' b m (s, w)
execRWSP i s p = fmap f $ runRWSP i s p
where
f x = let (_, s', w) = x in (s', w)
{-# INLINABLE execRWSP #-}
{- $tutorial
Probably the most useful functionality in this module is lifted error
handling. Suppose that you have a 'Pipes.Pipe' whose base monad can fail
using 'E.ExceptT':
> import Control.Monad.Trans.Error
> import Pipes
>
> example :: Monad m => Pipe Int Int (ExceptT String m) r
> example = for cat $ \n ->
> if n == 0
> then lift $ throwError "Zero is forbidden"
> else yield n
Without the tools in this module you cannot recover from any potential error
until after you compose and run the pipeline:
>>> import qualified Pipes.Prelude as P
>>> runExceptT $ runEffect $ P.readLn >-> example >-> P.print
42<Enter>
42
1<Enter>
1
0<Enter>
Zero is forbidden
>>>
This module provides `catchError`, which lets you catch and recover from
errors inside the 'Pipe':
> import qualified Pipes.Lift as Lift
>
> caught :: Pipe Int Int (ExceptT String IO) r
> caught = example `Lift.catchError` \str -> do
> liftIO (putStrLn str)
> caught
This lets you resume streaming in the face of errors raised within the base
monad:
>>> runExceptT $ runEffect $ P.readLn >-> caught >-> P.print
0<Enter>
Zero is forbidden
42<Enter>
42
0<Enter>
Zero is forbidden
1<Enter>
1
...
Another common use case is running a base monad before running the pipeline.
For example, the following contrived 'Producer' uses 'S.StateT' gratuitously
to increment numbers:
> import Control.Monad (forever)
> import Control.Monad.Trans.State.Strict
> import Pipes
>
> numbers :: Monad m => Producer Int (StateT Int m) r
> numbers = forever $ do
> n <- lift get
> yield n
> lift $ put $! n + 1
You can run the 'StateT' monad by supplying an initial state, before you
ever compose the 'Producer':
> import Pipes.Lift
>
> naturals :: Monad m => Producer Int m r
> naturals = evalStateP 0 numbers
This deletes 'StateT' from the base monad entirely, give you a completely
pure 'Pipes.Producer':
>>> Pipes.Prelude.toList naturals
[0,1,2,3,4,5,6...]
Note that the convention for the 'S.StateT' run functions is backwards from
@transformers@ for convenience: the initial state is the first argument.
All of these functions internally use 'distribute', which can pull out most
monad transformers from the base monad. For example, 'evalStateP' is
defined in terms of 'distribute':
> evalStateP s p = evalStateT (distribute p) s
Therefore you can use 'distribute' to run other monad transformers, too, as
long as they implement the 'MFunctor' type class from the @mmorph@ library.
-}
================================================
FILE: src/Pipes/Prelude.hs
================================================
{-| General purpose utilities
The names in this module clash heavily with the Haskell Prelude, so I
recommend the following import scheme:
> import Pipes
> import qualified Pipes.Prelude as P -- or use any other qualifier you prefer
Note that 'String'-based 'IO' is inefficient. The 'String'-based utilities
in this module exist only for simple demonstrations without incurring a
dependency on the @text@ package.
Also, 'stdinLn' and 'stdoutLn' remove and add newlines, respectively. This
behavior is intended to simplify examples. The corresponding @stdin@ and
@stdout@ utilities from @pipes-bytestring@ and @pipes-text@ preserve
newlines.
-}
{-# LANGUAGE RankNTypes, Trustworthy #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Pipes.Prelude (
-- * Producers
-- $producers
stdinLn
, readLn
, fromHandle
, repeatM
, replicateM
, unfoldr
-- * Consumers
-- $consumers
, stdoutLn
, stdoutLn'
, mapM_
, print
, toHandle
, drain
-- * Pipes
-- $pipes
, map
, mapM
, sequence
, mapFoldable
, filter
, mapMaybe
, filterM
, wither
, take
, takeWhile
, takeWhile'
, drop
, dropWhile
, concat
, elemIndices
, findIndices
, scan
, scanM
, chain
, read
, show
, seq
-- *ListT
, loop
-- * Folds
-- $folds
, fold
, fold'
, foldM
, foldM'
, all
, any
, and
, or
, elem
, notElem
, find
, findIndex
, head
, index
, last
, length
, maximum
, minimum
, null
, sum
, product
, toList
, toListM
, toListM'
-- * Zips
, zip
, zipWith
-- * Utilities
, tee
, generalize
) where
import Control.Exception (throwIO, try)
import Control.Monad (liftM, when, unless, (>=>))
import Control.Monad.Trans.State.Strict (get, put)
import Data.Functor.Identity (Identity, runIdentity)
import Foreign.C.Error (Errno(Errno), ePIPE)
import GHC.Exts (build)
import Pipes
import Pipes.Core
import Pipes.Internal
import Pipes.Lift (evalStateP)
import qualified GHC.IO.Exception as G
import qualified System.IO as IO
import qualified Prelude
import Prelude hiding (
all
, and
, any
, concat
, drop
, dropWhile
, elem
, filter
, head
, last
, length
, map
, mapM
, mapM_
, maximum
, minimum
, notElem
, null
, or
, print
, product
, read
, readLn
, sequence
, show
, seq
, sum
, take
, takeWhile
, zip
, zipWith
)
{- $producers
Use 'for' loops to iterate over 'Producer's whenever you want to perform the
same action for every element:
> -- Echo all lines from standard input to standard output
> runEffect $ for P.stdinLn $ \str -> do
> lift $ putStrLn str
... or more concisely:
>>> runEffect $ for P.stdinLn (lift . putStrLn)
Test<Enter>
Test
ABC<Enter>
ABC
...
-}
{-| Read 'String's from 'IO.stdin' using 'getLine'
Terminates on end of input
-}
stdinLn :: MonadIO m => Producer' String m ()
stdinLn = fromHandle IO.stdin
{-# INLINABLE stdinLn #-}
-- | 'read' values from 'IO.stdin', ignoring failed parses
readLn :: (MonadIO m, Read a) => Producer' a m ()
readLn = stdinLn >-> read
{-# INLINABLE readLn #-}
{-| Read 'String's from a 'IO.Handle' using 'IO.hGetLine'
Terminates on end of input
@
'fromHandle' :: 'MonadIO' m => 'IO.Handle' -> 'Producer' 'String' m ()
@
-}
fromHandle :: MonadIO m => IO.Handle -> Proxy x' x () String m ()
fromHandle h = go
where
go = do
eof <- liftIO $ IO.hIsEOF h
unless eof $ do
str <- liftIO $ IO.hGetLine h
yield str
go
{-# INLINABLE fromHandle #-}
{-| Repeat a monadic action indefinitely, 'yield'ing each result
'repeatM' :: 'Monad' m => m a -> 'Producer' a m r
-}
repeatM :: Monad m => m a -> Proxy x' x () a m r
repeatM m = lift m >~ cat
{-# INLINABLE [1] repeatM #-}
{-# RULES
"repeatM m >-> p" forall m p . repeatM m >-> p = lift m >~ p
#-}
{-| Repeat a monadic action a fixed number of times, 'yield'ing each result
> replicateM 0 x = return ()
>
> replicateM (m + n) x = replicateM m x >> replicateM n x -- 0 <= {m,n}
@
'replicateM' :: 'Monad' m => Int -> m a -> 'Producer' a m ()
@
-}
replicateM :: Monad m => Int -> m a -> Proxy x' x () a m ()
replicateM n m = lift m >~ take n
{-# INLINABLE replicateM #-}
{- $consumers
Feed a 'Consumer' the same value repeatedly using ('>~'):
>>> runEffect $ lift getLine >~ P.stdoutLn
Test<Enter>
Test
ABC<Enter>
ABC
...
-}
{-| Write 'String's to 'IO.stdout' using 'putStrLn'
Unlike 'toHandle', 'stdoutLn' gracefully terminates on a broken output pipe
-}
stdoutLn :: MonadIO m => Consumer' String m ()
stdoutLn = go
where
go = do
str <- await
x <- liftIO $ try (putStrLn str)
case x of
Left (G.IOError { G.ioe_type = G.ResourceVanished
, G.ioe_errno = Just ioe })
| Errno ioe == ePIPE
-> return ()
Left e -> liftIO (throwIO e)
Right () -> go
{-# INLINABLE stdoutLn #-}
{-| Write 'String's to 'IO.stdout' using 'putStrLn'
This does not handle a broken output pipe, but has a polymorphic return
value
-}
stdoutLn' :: MonadIO m => Consumer' String m r
stdoutLn' = for cat (\str -> liftIO (putStrLn str))
{-# INLINABLE [1] stdoutLn' #-}
{-# RULES
"p >-> stdoutLn'" forall p .
p >-> stdoutLn' = for p (\str -> liftIO (putStrLn str))
#-}
-- | Consume all values using a monadic function
mapM_ :: Monad m => (a -> m ()) -> Consumer' a m r
mapM_ f = for cat (\a -> lift (f a))
{-# INLINABLE [1] mapM_ #-}
{-# RULES
"p >-> mapM_ f" forall p f .
p >-> mapM_ f = for p (\a -> lift (f a))
#-}
-- | 'print' values to 'IO.stdout'
print :: (MonadIO m, Show a) => Consumer' a m r
print = for cat (\a -> liftIO (Prelude.print a))
{-# INLINABLE [1] print #-}
{-# RULES
"p >-> print" forall p .
p >-> print = for p (\a -> liftIO (Prelude.print a))
#-}
-- | Write 'String's to a 'IO.Handle' using 'IO.hPutStrLn'
toHandle :: MonadIO m => IO.Handle -> Consumer' String m r
toHandle handle = for cat (\str -> liftIO (IO.hPutStrLn handle str))
{-# INLINABLE [1] toHandle #-}
{-# RULES
"p >-> toHandle handle" forall p handle .
p >-> toHandle handle = for p (\str -> liftIO (IO.hPutStrLn handle str))
#-}
-- | 'discard' all incoming values
drain :: Functor m => Consumer' a m r
drain = for cat discard
{-# INLINABLE [1] drain #-}
{-# RULES
"p >-> drain" forall p .
p >-> drain = for p discard
#-}
{- $pipes
Use ('>->') to connect 'Producer's, 'Pipe's, and 'Consumer's:
>>> runEffect $ P.stdinLn >-> P.takeWhile (/= "quit") >-> P.stdoutLn
Test<Enter>
Test
ABC<Enter>
ABC
quit<Enter>
>>>
-}
{-| Apply a function to all values flowing downstream
> map id = cat
>
> map (g . f) = map f >-> map g
-}
map :: Functor m => (a -> b) -> Pipe a b m r
map f = for cat (\a -> yield (f a))
{-# INLINABLE [1] map #-}
{-# RULES
"p >-> map f" forall p f . p >-> map f = for p (\a -> yield (f a))
; "map f >-> p" forall p f . map f >-> p = (do
a <- await
return (f a) ) >~ p
#-}
{-| Apply a monadic function to all values flowing downstream
> mapM return = cat
>
> mapM (f >=> g) = mapM f >-> mapM g
-}
mapM :: Monad m => (a -> m b) -> Pipe a b m r
mapM f = for cat $ \a -> do
b <- lift (f a)
yield b
{-# INLINABLE [1] mapM #-}
{-# RULES
"p >-> mapM f" forall p f . p >-> mapM f = for p (\a -> do
b <- lift (f a)
yield b )
; "mapM f >-> p" forall p f . mapM f >-> p = (do
a <- await
b <- lift (f a)
return b ) >~ p
#-}
-- | Convert a stream of actions to a stream of values
sequence :: Monad m => Pipe (m a) a m r
sequence = mapM id
{-# INLINABLE sequence #-}
{- | Apply a function to all values flowing downstream, and
forward each element of the result.
-}
mapFoldable :: (Functor m, Foldable t) => (a -> t b) -> Pipe a b m r
mapFoldable f = for cat (\a -> each (f a))
{-# INLINABLE [1] mapFoldable #-}
{-# RULES
"p >-> mapFoldable f" forall p f .
p >-> mapFoldable f = for p (\a -> each (f a))
#-}
{-| @(filter predicate)@ only forwards values that satisfy the predicate.
> filter (pure True) = cat
>
> filter (liftA2 (&&) p1 p2) = filter p1 >-> filter p2
>
> filter f = mapMaybe (\a -> a <$ guard (f a))
-}
filter :: Functor m => (a -> Bool) -> Pipe a a m r
filter predicate = for cat $ \a -> when (predicate a) (yield a)
{-# INLINABLE [1] filter #-}
{-# RULES
"p >-> filter predicate" forall p predicate.
p >-> filter predicate = for p (\a -> when (predicate a) (yield a))
#-}
{-| @(mapMaybe f)@ yields 'Just' results of 'f'.
Basic laws:
> mapMaybe (f >=> g) = mapMaybe f >-> mapMaybe g
>
> mapMaybe (pure @Maybe . f) = mapMaybe (Just . f) = map f
>
> mapMaybe (const Nothing) = drain
As a result of the second law,
> mapMaybe return = mapMaybe Just = cat
-}
mapMaybe :: Functor m => (a -> Maybe b) -> Pipe a b m r
mapMaybe f = for cat $ maybe (pure ()) yield . f
{-# INLINABLE [1] mapMaybe #-}
{-# RULES
"p >-> mapMaybe f" forall p f.
p >-> mapMaybe f = for p $ maybe (pure ()) yield . f
#-}
{-| @(filterM predicate)@ only forwards values that satisfy the monadic
predicate
> filterM (pure (pure True)) = cat
>
> filterM (liftA2 (liftA2 (&&)) p1 p2) = filterM p1 >-> filterM p2
>
> filterM f = wither (\a -> (\b -> a <$ guard b) <$> f a)
-}
filterM :: Monad m => (a -> m Bool) -> Pipe a a m r
filterM predicate = for cat $ \a -> do
b <- lift (predicate a)
when b (yield a)
{-# INLINABLE [1] filterM #-}
{-# RULES
"p >-> filterM predicate" forall p predicate .
p >-> filterM predicate = for p (\a -> do
b <- lift (predicate a)
when b (yield a) )
#-}
{-| @(wither f)@ forwards 'Just' values produced by the
monadic action.
Basic laws:
> wither (runMaybeT . (MaybeT . f >=> MaybeT . g)) = wither f >-> wither g
>
> wither (runMaybeT . lift . f) = wither (fmap Just . f) = mapM f
>
> wither (pure . f) = mapMaybe f
As a result of the second law,
> wither (runMaybeT . return) = cat
As a result of the third law,
> wither (pure . const Nothing) = wither (const (pure Nothing)) = drain
-}
wither :: Monad m => (a -> m (Maybe b)) -> Pipe a b m r
wither f = for cat $ lift . f >=> maybe (pure ()) yield
{-# INLINABLE [1] wither #-}
{-# RULES
"p >-> wither f" forall p f .
p >-> wither f = for p $ lift . f >=> maybe (pure ()) yield
#-}
{-| @(take n)@ only allows @n@ values to pass through
> take 0 = return ()
>
> take (m + n) = take m >> take n
> take <infinity> = cat
>
> take (min m n) = take m >-> take n
-}
take :: Functor m => Int -> Pipe a a m ()
take = go
where
go 0 = return ()
go n = do
a <- await
yield a
go (n-1)
{-# INLINABLE take #-}
{-| @(takeWhile p)@ allows values to pass downstream so long as they satisfy
the predicate @p@.
> takeWhile (pure True) = cat
>
> takeWhile (liftA2 (&&) p1 p2) = takeWhile p1 >-> takeWhile p2
-}
takeWhile :: Functor m => (a -> Bool) -> Pipe a a m ()
takeWhile predicate = go
where
go = do
a <- await
if (predicate a)
then do
yield a
go
else return ()
{-# INLINABLE takeWhile #-}
{-| @(takeWhile' p)@ is a version of takeWhile that returns the value failing
the predicate.
> takeWhile' (pure True) = cat
>
> takeWhile' (liftA2 (&&) p1 p2) = takeWhile' p1 >-> takeWhile' p2
-}
takeWhile' :: Functor m => (a -> Bool) -> Pipe a a m a
takeWhile' predicate = go
where
go = do
a <- await
if (predicate a)
then do
yield a
go
else return a
{-# INLINABLE takeWhile' #-}
{-| @(drop n)@ discards @n@ values going downstream
> drop 0 = cat
>
> drop (m + n) = drop m >-> drop n
-}
drop :: Functor m => Int -> Pipe a a m r
drop = go
where
go 0 = cat
go n = do
await
go (n-1)
{-# INLINABLE drop #-}
{-| @(dropWhile p)@ discards values going downstream until one violates the
predicate @p@.
> dropWhile (pure False) = cat
>
> dropWhile (liftA2 (||) p1 p2) = dropWhile p1 >-> dropWhile p2
-}
dropWhile :: Functor m => (a -> Bool) -> Pipe a a m r
dropWhile predicate = go
where
go = do
a <- await
if (predicate a)
then go
else do
yield a
cat
{-# INLINABLE dropWhile #-}
-- | Flatten all 'Foldable' elements flowing downstream
concat :: (Functor m, Foldable f) => Pipe (f a) a m r
concat = for cat each
{-# INLINABLE [1] concat #-}
{-# RULES
"p >-> concat" forall p . p >-> concat = for p each
#-}
-- | Outputs the indices of all elements that match the given element
elemIndices :: (Functor m, Eq a) => a -> Pipe a Int m r
elemIndices a = findIndices (a ==)
{-# INLINABLE elemIndices #-}
-- | Outputs the indices of all elements that satisfied the predicate
findIndices :: Functor m => (a -> Bool) -> Pipe a Int m r
findIndices predicate = go 0
where
go n = do
a <- await
when (predicate a) (yield n)
go $! n + 1
{-# INLINABLE findIndices #-}
{-| Strict left scan
> Control.Foldl.purely scan :: Monad m => Fold a b -> Pipe a b m r
-}
scan :: Functor m => (x -> a -> x) -> x -> (x -> b) -> Pipe a b m r
scan step begin done = go begin
where
go x = do
yield (done x)
a <- await
let x' = step x a
go $! x'
{-# INLINABLE scan #-}
{-| Strict, monadic left scan
> Control.Foldl.impurely scanM :: Monad m => FoldM m a b -> Pipe a b m r
-}
scanM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Pipe a b m r
scanM step begin done = do
x <- lift begin
go x
where
go x = do
b <- lift (done x)
yield b
a <- await
x' <- lift (step x a)
go $! x'
{-# INLINABLE scanM #-}
{-| Apply an action to all values flowing downstream
> chain (pure (return ())) = cat
>
> chain (liftA2 (>>) m1 m2) = chain m1 >-> chain m2
-}
chain :: Monad m => (a -> m ()) -> Pipe a a m r
chain f = for cat $ \a -> do
lift (f a)
yield a
{-# INLINABLE [1] chain #-}
{-# RULES
"p >-> chain f" forall p f .
p >-> chain f = for p (\a -> do
lift (f a)
yield a )
; "chain f >-> p" forall p f .
chain f >-> p = (do
a <- await
lift (f a)
return a ) >~ p
#-}
-- | Parse 'Read'able values, only forwarding the value if the parse succeeds
read :: (Functor m, Read a) => Pipe String a m r
read = for cat $ \str -> case (reads str) of
[(a, "")] -> yield a
_ -> return ()
{-# INLINABLE [1] read #-}
{-# RULES
"p >-> read" forall p .
p >-> read = for p (\str -> case (reads str) of
[(a, "")] -> yield a
_ -> return () )
#-}
-- | Convert 'Show'able values to 'String's
show :: (Functor m, Show a) => Pipe a String m r
show = map Prelude.show
{-# INLINABLE show #-}
-- | Evaluate all values flowing downstream to WHNF
seq :: Functor m => Pipe a a m r
seq = for cat $ \a -> yield $! a
{-# INLINABLE seq #-}
{-| Create a `Pipe` from a `ListT` transformation
> loop (k1 >=> k2) = loop k1 >-> loop k2
>
> loop return = cat
-}
loop :: Monad m => (a -> ListT m b) -> Pipe a b m r
loop k = for cat (every . k)
{-# INLINABLE loop #-}
{- $folds
Use these to fold the output of a 'Producer'. Many of these folds will stop
drawing elements if they can compute their result early, like 'any':
>>> P.any Prelude.null P.stdinLn
Test<Enter>
ABC<Enter>
<Enter>
True
>>>
-}
{-| Strict fold of the elements of a 'Producer'
> Control.Foldl.purely fold :: Monad m => Fold a b -> Producer a m () -> m b
-}
fold :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Producer a m () -> m b
fold step begin done p0 = go p0 begin
where
go p x = case p of
Request v _ -> closed v
Respond a fu -> go (fu ()) $! step x a
M m -> m >>= \p' -> go p' x
Pure _ -> return (done x)
{-# INLINABLE fold #-}
{-| Strict fold of the elements of a 'Producer' that preserves the return value
> Control.Foldl.purely fold' :: Monad m => Fold a b -> Producer a m r -> m (b, r)
-}
fold' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Producer a m r -> m (b, r)
fold' step begin done p0 = go p0 begin
where
go p x = case p of
Request v _ -> closed v
Respond a fu -> go (fu ()) $! step x a
M m -> m >>= \p' -> go p' x
Pure r -> return (done x, r)
{-# INLINABLE fold' #-}
{-| Strict, monadic fold of the elements of a 'Producer'
> Control.Foldl.impurely foldM :: Monad m => FoldM a b -> Producer a m () -> m b
-}
foldM
:: Monad m
=> (x -> a -> m x) -> m x -> (x -> m b) -> Producer a m () -> m b
foldM step begin done p0 = do
x0 <- begin
go p0 x0
where
go p x = case p of
Request v _ -> closed v
Respond a fu -> do
x' <- step x a
go (fu ()) $! x'
M m -> m >>= \p' -> go p' x
Pure _ -> done x
{-# INLINABLE foldM #-}
{-| Strict, monadic fold of the elements of a 'Producer'
> Control.Foldl.impurely foldM' :: Monad m => FoldM a b -> Producer a m r -> m (b, r)
-}
foldM'
:: Monad m
=> (x -> a -> m x) -> m x -> (x -> m b) -> Producer a m r -> m (b, r)
foldM' step begin done p0 = do
x0 <- begin
go p0 x0
where
go p x = case p of
Request v _ -> closed v
Respond a fu -> do
x' <- step x a
go (fu ()) $! x'
M m -> m >>= \p' -> go p' x
Pure r -> do
b <- done x
return (b, r)
{-# INLINABLE foldM' #-}
{-| @(all predicate p)@ determines whether all the elements of @p@ satisfy the
predicate.
-}
all :: Monad m => (a -> Bool) -> Producer a m () -> m Bool
all predicate p = null $ p >-> filter (\a -> not (predicate a))
{-# INLINABLE all #-}
{-| @(any predicate p)@ determines whether any element of @p@ satisfies the
predicate.
-}
any :: Monad m => (a -> Bool) -> Producer a m () -> m Bool
any predicate p = liftM not $ null (p >-> filter predicate)
{-# INLINABLE any #-}
-- | Determines whether all elements are 'True'
and :: Monad m => Producer Bool m () -> m Bool
and = all id
{-# INLINABLE and #-}
-- | Determines whether any element is 'True'
or :: Monad m => Producer Bool m () -> m Bool
or = any id
{-# INLINABLE or #-}
{-| @(elem a p)@ returns 'True' if @p@ has an element equal to @a@, 'False'
otherwise
-}
elem :: (Monad m, Eq a) => a -> Producer a m () -> m Bool
elem a = any (a ==)
{-# INLINABLE elem #-}
{-| @(notElem a)@ returns 'False' if @p@ has an element equal to @a@, 'True'
otherwise
-}
notElem :: (Monad m, Eq a) => a -> Producer a m () -> m Bool
notElem a = all (a /=)
{-# INLINABLE notElem #-}
-- | Find the first element of a 'Producer' that satisfies the predicate
find :: Monad m => (a -> Bool) -> Producer a m () -> m (Maybe a)
find predicate p = head (p >-> filter predicate)
{-# INLINABLE find #-}
{-| Find the index of the first element of a 'Producer' that satisfies the
predicate
-}
findIndex :: Monad m => (a -> Bool) -> Producer a m () -> m (Maybe Int)
findIndex predicate p = head (p >-> findIndices predicate)
{-# INLINABLE findIndex #-}
-- | Retrieve the first element from a 'Producer'
head :: Monad m => Producer a m () -> m (Maybe a)
head p = do
x <- next p
return $ case x of
Left _ -> Nothing
Right (a, _) -> Just a
{-# INLINABLE head #-}
-- | Index into a 'Producer'
index :: Monad m => Int -> Producer a m () -> m (Maybe a)
index n p = head (p >-> drop n)
{-# INLINABLE index #-}
-- | Retrieve the last element from a 'Producer'
last :: Monad m => Producer a m () -> m (Maybe a)
last p0 = do
x <- next p0
case x of
Left _ -> return Nothing
Right (a, p') -> go a p'
where
go a p = do
x <- next p
case x of
Left _ -> return (Just a)
Right (a', p') -> go a' p'
{-# INLINABLE last #-}
-- | Count the number of elements in a 'Producer'
length :: Monad m => Producer a m () -> m Int
length = fold (\n _ -> n + 1) 0 id
{-# INLINABLE length #-}
-- | Find the maximum element of a 'Producer'
maximum :: (Monad m, Ord a) => Producer a m () -> m (Maybe a)
maximum = fold step Nothing id
where
step x a = Just $ case x of
Nothing -> a
Just a' -> max a a'
{-# INLINABLE maximum #-}
-- | Find the minimum element of a 'Producer'
minimum :: (Monad m, Ord a) => Producer a m () -> m (Maybe a)
minimum = fold step Nothing id
where
step x a = Just $ case x of
Nothing -> a
Just a' -> min a a'
{-# INLINABLE minimum #-}
-- | Determine if a 'Producer' is empty
null :: Monad m => Producer a m () -> m Bool
null p = do
x <- next p
return $ case x of
Left _ -> True
Right _ -> False
{-# INLINABLE null #-}
-- | Compute the sum of the elements of a 'Producer'
sum :: (Monad m, Num a) => Producer a m () -> m a
sum = fold (+) 0 id
{-# INLINABLE sum #-}
-- | Compute the product of the elements of a 'Producer'
product :: (Monad m, Num a) => Producer a m () -> m a
product = fold (*) 1 id
{-# INLINABLE product #-}
-- | Convert a pure 'Producer' into a list
toList :: Producer a Identity () -> [a]
toList prod0 = build (go prod0)
where
go prod cons nil =
case prod of
Request v _ -> closed v
Respond a fu -> cons a (go (fu ()) cons nil)
M m -> go (runIdentity m) cons nil
Pure _ -> nil
{-# INLINE toList #-}
{-| Convert an effectful 'Producer' into a list
Note: 'toListM' is not an idiomatic use of @pipes@, but I provide it for
simple testing purposes. Idiomatic @pipes@ style consumes the elements
immediately as they are generated instead of loading all elements into
memory.
-}
toListM :: Monad m => Producer a m () -> m [a]
toListM = fold step begin done
where
step x a = x . (a:)
begin = id
done x = x []
{-# INLINABLE toListM #-}
{-| Convert an effectful 'Producer' into a list alongside the return value
Note: 'toListM'' is not an idiomatic use of @pipes@, but I provide it for
simple testing purposes. Idiomatic @pipes@ style consumes the elements
immediately as they are generated instead of loading all elements into
memory.
-}
toListM' :: Monad m => Producer a m r -> m ([a], r)
toListM' = fold' step begin done
where
step x a = x . (a:)
begin = id
done x = x []
{-# INLINABLE toListM' #-}
-- | Zip two 'Producer's
zip :: Monad m
=> (Producer a m r)
-> (Producer b m r)
-> (Proxy x' x () (a, b) m r)
zip = zipWith (,)
{-# INLINABLE zip #-}
-- | Zip two 'Producer's using the provided combining function
zipWith :: Monad m
=> (a -> b -> c)
-> (Producer a m r)
-> (Producer b m r)
-> (Proxy x' x () c m r)
zipWith f = go
where
go p1 p2 = do
e1 <- lift $ next p1
case e1 of
Left r -> return r
Right (a, p1') -> do
e2 <- lift $ next p2
case e2 of
Left r -> return r
Right (b, p2') -> do
yield (f a b)
go p1' p2'
{-# INLINABLE zipWith #-}
{-| Transform a 'Consumer' to a 'Pipe' that reforwards all values further
downstream
-}
tee :: Monad m => Consumer a m r -> Pipe a a m r
tee p = evalStateP Nothing $ do
r <- up >\\ (hoist lift p //> dn)
ma <- lift get
case ma of
Nothing -> return ()
Just a -> yield a
return r
where
up () = do
ma <- lift get
case ma of
Nothing -> return ()
Just a -> yield a
a <- await
lift $ put (Just a)
return a
dn v = closed v
{-# INLINABLE tee #-}
{-| Transform a unidirectional 'Pipe' to a bidirectional 'Proxy'
> generalize (f >-> g) = generalize f >+> generalize g
>
> generalize cat = pull
-}
generalize :: Monad m => Pipe a b m r -> x -> Proxy x a x b m r
generalize p x0 = evalStateP x0 $ up >\\ hoist lift p //> dn
where
up () = do
x <- lift get
request x
dn a = do
x <- respond a
lift $ put x
{-# INLINABLE generalize #-}
{-| The natural unfold into a 'Producer' with a step function and a seed
> unfoldr next = id
-}
unfoldr :: Monad m
=> (s -> m (Either r (a, s))) -> s -> Producer a m r
unfoldr step = go where
go s0 = do
e <- lift (step s0)
case e of
Left r -> return r
Right (a,s) -> do
yield a
go s
{-# INLINABLE unfoldr #-}
================================================
FILE: src/Pipes/Tutorial.hs
================================================
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-| Conventional Haskell stream programming forces you to choose only two of the
following three features:
* Effects
* Streaming
* Composability
If you sacrifice /Effects/ you get Haskell's pure and lazy lists, which you
can transform using composable functions in constant space, but without
interleaving effects.
If you sacrifice /Streaming/ you get 'mapM', 'forM' and
\"ListT done wrong\", which are composable and effectful, but do not return
a single result until the whole list has first been processed and loaded
into memory.
If you sacrifice /Composability/ you write a tightly coupled read,
transform, and write loop in 'IO', which is streaming and effectful, but is
not modular or separable.
@pipes@ gives you all three features: effectful, streaming, and composable
programming. @pipes@ also provides a wide variety of stream programming
abstractions which are all subsets of a single unified machinery:
* effectful 'Producer's (like generators),
* effectful 'Consumer's (like iteratees),
* effectful 'Pipe's (like Unix pipes), and:
* 'ListT' done right.
All of these are connectable and you can combine them together in clever and
unexpected ways because they all share the same underlying type.
@pipes@ requires a basic understanding of monad transformers, which you can
learn about by reading either:
* the paper \"Monad Transformers - Step by Step\",
* part III \"Monads in the Real World\" of the tutorial \"All About Monads\",
* chapter 18 of \"Real World Haskell\" on monad transformers, or:
* the documentation of the @transformers@ library.
If you want a Quick Start guide to @pipes@, read the documentation in
"Pipes.Prelude" from top to bottom.
This tutorial is more extensive and explains the @pipes@ API in greater
detail and illustrates several idioms.
-}
module Pipes.Tutorial (
-- * Introduction
-- $introduction
-- * Producers
-- $producers
-- * Composability
-- $composability
-- * Consumers
-- $consumers
-- * Pipes
-- $pipes
-- * ListT
-- $listT
-- * Tricks
-- $tricks
-- * Conclusion
-- $conclusion
-- * Appendix: Types
-- $types
-- * Appendix: Time Complexity
-- $timecomplexity
-- * Copyright
-- $copyright
) where
import Control.Category
import Control.Monad
import Pipes
import qualified Pipes.Prelude as P
import Prelude hiding ((.), id)
{- $introduction
The @pipes@ library decouples stream processing stages from each other so
that you can mix and match diverse stages to produce useful streaming
programs. If you are a library writer, @pipes@ lets you package up
streaming components into a reusable interface. If you are an application
writer, @pipes@ lets you connect pre-made streaming components with minimal
effort to produce a highly-efficient program that streams data in constant
memory.
To enforce loose coupling, components can only communicate using two
commands:
* 'yield': Send output data
* 'await': Receive input data
@pipes@ has four types of components built around these two commands:
* 'Producer's can only 'yield' values and they model streaming sources
* 'Consumer's can only 'await' values and they model streaming sinks
* 'Pipe's can both 'yield' and 'await' values and they model stream
transformations
* 'Effect's can neither 'yield' nor 'await' and they model non-streaming
components
You can connect these components together in four separate ways which
parallel the four above types:
* 'for' handles 'yield's
* ('>~') handles 'await's
* ('>->') handles both 'yield's and 'await's
* ('>>=') handles return values
As you connect components their types will change to reflect inputs and
outputs that you've fused away. You know that you're done connecting things
when you get an 'Effect', meaning that you have handled all inputs and
outputs. You run this final 'Effect' to begin streaming.
-}
{- $producers
'Producer's are effectful streams of input. Specifically, a 'Producer' is a
monad transformer that extends any base monad with a new 'yield' command.
This 'yield' command lets you send output downstream to an anonymous
handler, decoupling how you generate values from how you consume them.
The following @stdinLn@ 'Producer' shows how to incrementally read in
'String's from standard input and 'yield' them downstream, terminating
gracefully when reaching the end of the input:
> -- echo.hs
>
> import Control.Monad (unless)
> import Pipes
> import System.IO (isEOF)
>
> -- +--------+-- A 'Producer' that yields 'String's
> -- | |
> -- | | +-- Every monad transformer has a base monad.
> -- | | | This time the base monad is 'IO'.
> -- | | |
> -- | | | +-- Every monadic action has a return value.
> -- | | | | This action returns '()' when finished
> -- v v v v
> stdinLn :: Producer String IO ()
> stdinLn = do
> eof <- lift isEOF -- 'lift' an 'IO' action from the base monad
> unless eof $ do
> str <- lift getLine
> yield str -- 'yield' the 'String'
> stdinLn -- Loop
'yield' emits a value, suspending the current 'Producer' until the value is
consumed. If nobody consumes the value (which is possible) then 'yield'
never returns. You can think of 'yield' as having the following type:
@
'yield' :: 'Monad' m => a -> 'Producer' a m ()
@
The true type of 'yield' is actually more general and powerful. Throughout
the tutorial I will present type signatures like this that are simplified at
first and then later reveal more general versions. So read the above type
signature as simply saying: \"You can use 'yield' within a 'Producer', but
you may be able to use 'yield' in other contexts, too.\"
Click the link to 'yield' to navigate to its documentation. There you will
see that 'yield' actually uses the 'Producer'' (with an apostrophe) type
synonym which hides a lot of polymorphism behind a simple veneer. The
documentation for 'yield' says that you can also use 'yield' within a
'Pipe', too, because of this polymorphism:
@
'yield' :: 'Monad' m => a -> 'Pipe' x a m ()
@
Use simpler types like these to guide you until you understand the fully
general type.
'for' loops are the simplest way to consume a 'Producer' like @stdinLn@.
'for' has the following type:
@
\-\- +-- Producer +-- The body of the +-- Result
\-\- | to loop | loop |
\-\- v over v v
\-\- -------------- ------------------ ----------
'for' :: 'Monad' m => 'Producer' a m r -> (a -> 'Effect' m ()) -> 'Effect' m r
@
@(for producer body)@ loops over @(producer)@, substituting each 'yield' in
@(producer)@ with @(body)@.
You can also deduce that behavior purely from the type signature:
* The body of the loop takes exactly one argument of type @(a)@, which is
the same as the output type of the 'Producer'. Therefore, the body of the
loop must get its input from that 'Producer' and nowhere else.
* The return value of the input 'Producer' matches the return value of the
result, therefore 'for' must loop over the entire 'Producer' and not skip
anything.
The above type signature is not the true type of 'for', which is actually
more general. Think of the above type signature as saying: \"If the first
argument of 'for' is a 'Producer' and the second argument returns an
'Effect', then the final result must be an 'Effect'.\"
Click the link to 'for' to navigate to its documentation. There you will
see the fully general type and underneath you will see equivalent simpler
types. One of these says that if the body of the loop is a 'Producer', then
the result is a 'Producer', too:
@
'for' :: 'Monad' m => 'Producer' a m r -> (a -> 'Producer' b m ()) -> 'Producer' b m r
@
The first type signature I showed for 'for' was a special case of this
slightly more general signature because a 'Producer' that never 'yield's is
also an 'Effect':
@
data 'X' -- The uninhabited type
\ type 'Effect' m r = 'Producer' 'X' m r
@
This is why 'for' permits two different type signatures. The first type
signature is just a special case of the second one:
@
'for' :: 'Monad' m => 'Producer' a m r -> (a -> 'Producer' b m ()) -> 'Producer' b m r
\ -- Specialize \'b\' to \'X\'
'for' :: 'Monad' m => 'Producer' a m r -> (a -> 'Producer' 'X' m ()) -> 'Producer' 'X' m r
\ -- Producer X = Effect
'for' :: 'Monad' m => 'Producer' a m r -> (a -> 'Effect' m ()) -> 'Effect' m r
@
This is the same trick that all @pipes@ functions use to work with various
combinations of 'Producer's, 'Consumer's, 'Pipe's, and 'Effect's. Each
function really has just one general type, which you can then simplify down
to multiple useful alternative types.
Here's an example use of a 'for' @loop@, where the second argument (the
loop body) is an 'Effect':
> -- echo.hs
>
> loop :: Effect IO ()
> loop = for stdinLn $ \str -> do -- Read this like: "for str in stdinLn"
> lift $ putStrLn str -- The body of the 'for' loop
>
> -- more concise: loop = for stdinLn (lift . putStrLn)
In this example, 'for' loops over @stdinLn@ and replaces every 'yield' in
@stdinLn@ with the body of the loop, printing each line. This is exactly
equivalent to the following code, which I've placed side-by-side with the
original definition of @stdinLn@ for comparison:
> loop = do | stdinLn = do
> eof <- lift isEOF | eof <- lift isEOF
> unless eof $ do | unless eof $ do
> str <- lift getLine | str <- lift getLine
> (lift . putStrLn) str | yield str
> loop | stdinLn
You can think of 'yield' as creating a hole and a 'for' loop is one way to
fill that hole.
Notice how the final @loop@ only 'lift's actions from the base monad and
does nothing else. This property is true for all 'Effect's, which are just
glorified wrappers around actions in the base monad. This means we can run
these 'Effect's to remove their 'lift's and lower them back to the
equivalent computation in the base monad:
@
'runEffect' :: 'Monad' m => 'Effect' m r -> m r
@
This is the real type signature of 'runEffect', which refuses to accept
anything other than an 'Effect'. This ensures that we handle all inputs and
outputs before streaming data:
> -- echo.hs
>
> main :: IO ()
> main = runEffect loop
... or you could inline the entire @loop@ into the following one-liner:
> main = runEffect $ for stdinLn (lift . putStrLn)
Our final program loops over standard input and echoes every line to
standard output until we hit @Ctrl-D@ to end the input stream:
> $ ghc -O2 echo.hs
> $ ./echo
> Test<Enter>
> Test
> ABC<Enter>
> ABC
> <Ctrl-D>
> $
The final behavior is indistinguishable from just removing all the 'lift's
from @loop@:
> main = do | loop = do
> eof <- isEof | eof <- lift isEof
> unless eof $ do | unless eof $ do
> str <- getLine | str <- lift getLine
> putStrLn str | (lift . putStrLn) str
> main | loop
This @main@ is what we might have written by hand if we were not using
@pipes@, but with @pipes@ we can decouple the input and output logic from
each other. When we connect them back together, we still produce streaming
code equivalent to what a sufficiently careful Haskell programmer would
have written.
You can also use 'for' to loop over lists, too. To do so, convert the list
to a 'Producer' using 'each', which is exported by default from "Pipes":
> each :: Monad m => [a] -> Producer a m ()
> each as = mapM_ yield as
Combine 'for' and 'each' to iterate over lists using a \"foreach\" loop:
>>> runEffect $ for (each [1..4]) (lift . print)
1
2
3
4
'each' is actually more general and works for any 'Foldable':
@
'each' :: ('Monad' m, 'Foldable' f) => f a -> 'Producer' a m ()
@
So you can loop over any 'Foldable' container or even a 'Maybe':
>>> runEffect $ for (each (Just 1)) (lift . print)
1
-}
{- $composability
You might wonder why the body of a 'for' loop can be a 'Producer'. Let's
test out this feature by defining a new loop body that creates three copies
of every value:
> -- nested.hs
>
> import Pipes
> import qualified Pipes.Prelude as P -- Pipes.Prelude already has 'stdinLn'
>
> triple :: Monad m => a -> Producer a m ()
> triple x = do
> yield x
> yield x
> yield x
>
> loop :: Producer String IO ()
> loop = for P.stdinLn triple
>
> -- This is the exact same as:
> --
> -- loop = for P.stdinLn $ \x -> do
> -- yield x
> -- yield x
> -- yield x
This time our @loop@ is a 'Producer' that outputs 'String's, specifically
three copies of each line that we read from standard input. Since @loop@ is
a 'Producer' we cannot run it because there is still unhandled output.
However, we can use yet another 'for' to handle this new repeated stream:
> -- nested.hs
>
> main = runEffect $ for loop (lift . putStrLn)
This creates a program which echoes every line from standard input to
standard output three times:
> $ ./nested
> Test<Enter>
> Test
> Test
> Test
> ABC<Enter>
> ABC
> ABC
> ABC
> <Ctrl-D>
> $
But is this really necessary? Couldn't we have instead written this using a
nested for loop?
> main = runEffect $
> for P.stdinLn $ \str1 ->
> for (triple str1) $ \str2 ->
> lift $ putStrLn str2
Yes, we could have! In fact, this is a special case of the following
equality, which always holds no matter what:
@
\-\- s :: Monad m => 'Producer' a m () -- i.e. \'P.stdinLn\'
\-\- f :: Monad m => a -> 'Producer' b m () -- i.e. \'triple\'
\-\- g :: Monad m => b -> 'Producer' c m () -- i.e. \'(lift . putStrLn)\'
\ for (for s f) g = for s (\\x -> for (f x) g)
@
We can understand the rationale behind this equality if we first define the
following operator that is the point-free counterpart to 'for':
@
(~>) :: Monad m
=> (a -> 'Producer' b m ())
-> (b -> 'Producer' c m ())
-> (a -> 'Producer' c m ())
(f ~> g) x = for (f x) g
@
Using ('~>') (pronounced \"into\"), we can transform our original equality
into the following more symmetric equation:
@
f :: Monad m => a -> 'Producer' b m ()
g :: Monad m => b -> 'Producer' c m ()
h :: Monad m => c -> 'Producer' d m ()
\ \-\- Associativity
(f ~> g) ~> h = f ~> (g ~> h)
@
This looks just like an associativity law. In fact, ('~>') has another nice
property, which is that 'yield' is its left and right identity:
> -- Left Identity
> yield ~> f = f
> -- Right Identity
> f ~> yield = f
In other words, 'yield' and ('~>') form a 'Category', specifically the
generator category, where ('~>') plays the role of the composition operator
and 'yield' is the identity. If you don't know what a 'Category' is, that's
okay, and category theory is not a prerequisite for using @pipes@. All you
really need to know is that @pipes@ uses some simple category theory to keep
the API intuitive and easy to use.
Notice that if we translate the left identity law to use 'for' instead of
('~>') we get:
> for (yield x) f = f x
This just says that if you iterate over a pure single-element 'Producer',
then you could instead cut out the middle man and directly apply the body of
the loop to that single element.
If we translate the right identity law to use 'for' instead of ('~>') we
get:
> for s yield = s
This just says that if the only thing you do is re-'yield' every element of
a stream, you get back your original stream.
These three \"for loop\" laws summarize our intuition for how 'for' loops
should behave and because these are 'Category' laws in disguise that means
that 'Producer's are composable in a rigorous sense of the word.
In fact, we get more out of this than just a bunch of equations. We also
get a useful operator: ('~>'). We can use this operator to condense
our original code into the following more succinct form that composes two
transformations:
> main = runEffect $ for P.stdinLn (triple ~> lift . putStrLn)
This means that we can also choose to program in a more functional style and
think of stream processing in terms of composing transformations using
('~>') instead of nesting a bunch of 'for' loops.
The above example is a microcosm of the design philosophy behind the @pipes@
library:
* Define the API in terms of categories
* Specify expected behavior in terms of category laws
* Think compositionally instead of sequentially
-}
{- $consumers
Sometimes you don't want to use a 'for' loop because you don't want to consume
every element of a 'Producer' or because you don't want to process every
value of a 'Producer' the exact same way.
The most general solution is to externally iterate over the 'Producer' using
the 'next' command:
@
'next' :: 'Monad' m => 'Producer' a m r -> m ('Either' r (a, 'Producer' a m r))
@
Think of 'next' as pattern matching on the head of the 'Producer'. This
'Either' returns a 'Left' if the 'Producer' is done or it returns a 'Right'
containing the next value, @a@, along with the remainder of the 'Producer'.
However, sometimes we can get away with something a little more simple and
elegant, like a 'Consumer', which represents an effectful sink of values. A
'Consumer' is a monad transformer that extends the base monad with a new
'await' command. This 'await' command lets you receive input from an
anonymous upstream source.
The following @stdoutLn@ 'Consumer' shows how to incrementally 'await'
'String's and print them to standard output, terminating gracefully when
receiving a broken pipe error:
> import Control.Monad (unless)
> import Control.Exception (try, throwIO)
> import qualified GHC.IO.Exception as G
> import Pipes
>
> -- +--------+-- A 'Consumer' that awaits 'String's
> -- | |
> -- v v
> stdoutLn :: Consumer String IO ()
> stdoutLn = do
> str <- await -- 'await' a 'String'
> x <- lift $ try $ putStrLn str
> case x of
> -- Gracefully terminate if we got a broken pipe error
> Left e@(G.IOError { G.ioe_type = t}) ->
> lift $ unless (t == G.ResourceVanished) $ throwIO e
> -- Otherwise loop
> Right () -> stdoutLn
'await' is the dual of 'yield': we suspend our 'Consumer' until we receive a
new value. If nobody provides a value (which is possible) then 'await'
never returns. You can think of 'await' as having the following type:
@
'await' :: 'Monad' m => 'Consumer' a m a
@
One way to feed a 'Consumer' is to repeatedly feed the same input using
('>~') (pronounced \"feed\"):
@
\-\- +- Feed +- Consumer to +- Returns new
\-\- | action | feed | Effect
\-\- v v v
\-\- ---------- -------------- ----------
('>~') :: 'Monad' m => 'Effect' m b -> 'Consumer' b m c -> 'Effect' m c
@
@(draw >~ consumer)@ loops over @(consumer)@, substituting each 'await' in
@(consumer)@ with @(draw)@.
So the following code replaces every 'await' in 'P.stdoutLn' with
@(lift getLine)@ and then removes all the 'lift's:
>>> runEffect $ lift getLine >~ stdoutLn
Test<Enter>
Test
ABC<Enter>
ABC
42<Enter>
42
...
You might wonder why ('>~') uses an 'Effect' instead of a raw action in the
base monad. The reason why is that ('>~') actually permits the following
more general type:
@
('>~') :: 'Monad' m => 'Consumer' a m b -> 'Consumer' b m c -> 'Consumer' a m c
@
('>~') is the dual of ('~>'), composing 'Consumer's instead of 'Producer's.
This means that you can feed a 'Consumer' with yet another 'Consumer' so
that you can 'await' while you 'await'. For example, we could define the
following intermediate 'Consumer' that requests two 'String's and returns
them concatenated:
> doubleUp :: Monad m => Consumer String m String
> doubleUp = do
> str1 <- await
> str2 <- await
> return (str1 ++ str2)
>
> -- more concise: doubleUp = (++) <$> await <*> await
We can now insert this in between @(lift getLine)@ and @stdoutLn@ and see
what happens:
>>> runEffect $ lift getLine >~ doubleUp >~ stdoutLn
Test<Enter>
ing<Enter>
Testing
ABC<Enter>
DEF<Enter>
ABCDEF
42<Enter>
000<Enter>
42000
...
'doubleUp' splits every request from 'stdoutLn' into two separate requests
and
returns back the concatenated result.
We didn't need to parenthesize the above chain of ('>~') operators, because
('>~') is associative:
> -- Associativity
> (f >~ g) >~ h = f >~ (g >~ h)
... so we can always omit the parentheses since the meaning is unambiguous:
> f >~ g >~ h
Also, ('>~') has an identity, which is 'await'!
> -- Left identity
> await >~ f = f
>
> -- Right Identity
> f >~ await = f
In other words, ('>~') and 'await' form a 'Category', too, specifically the
iteratee category, and 'Consumer's are also composable.
-}
{- $pipes
Our previous programs were unsatisfactory because they were biased either
towards the 'Producer' end or the 'Consumer' end. As a result, we had to
choose between gracefully handling end of input (using 'P.stdinLn') or
gracefully handling end of output (using 'P.stdoutLn'), but not both at the
same time.
However, we don't need to restrict ourselves to using 'Producer's
exclusively or 'Consumer's exclusively. We can connect 'Producer's and
'Consumer's directly together using ('>->') (pronounced \"pipe\"):
@
('>->') :: 'Monad' m => 'Producer' a m r -> 'Consumer' a m r -> 'Effect' m r
@
This returns an 'Effect' which we can run:
> -- echo2.hs
>
> import Pipes
> import qualified Pipes.Prelude as P -- Pipes.Prelude also provides 'stdoutLn'
>
> main = runEffect $ P.stdinLn >-> P.stdoutLn
This program is more declarative of our intent: we want to stream values
from 'P.stdinLn' to 'P.stdoutLn'. The above \"pipeline\" not only echoes
standard input to standard output, but also handles both end of input and
broken pipe errors:
> $ ./echo2
> Test<Enter>
> Test
> ABC<Enter>
> ABC
> 42<Enter>
> 42
> <Ctrl-D>
> $
('>->') is \"pull-based\" meaning that control flow begins at the most
downstream component (i.e. 'P.stdoutLn' in the above example). Any time a
component 'await's a value it blocks and transfers control upstream and
every time a component 'yield's a value it blocks and restores control back
downstream, satisfying the 'await'. So in the above example, ('>->')
matches every 'await' from 'P.stdoutLn' with a 'yield' from 'P.stdinLn'.
Streaming stops when either 'P.stdinLn' terminates (i.e. end of input) or
'P.stdoutLn' terminates (i.e. broken pipe). This is why ('>->') requires
that both the 'Producer' and 'Consumer' share the same type of return value:
whichever one terminates first provides the return value for the entire
'Effect'.
Let's test this by modifying our 'Producer' and 'Consumer' to each return a
diagnostic 'String':
> -- echo3.hs
>
> import Control.Applicative ((<$)) -- (<$) modifies return values
> import Pipes
> import qualified Pipes.Prelude as P
> import System.IO
>
> main = do
> hSetBuffering stdout NoBuffering
> str <- runEffect $
> ("End of input!" <$ P.stdinLn) >-> ("Broken pipe!" <$ P.stdoutLn)
> hPutStrLn stderr str
This lets us diagnose whether the 'Producer' or 'Consumer' terminated first:
> $ ./echo3
> Test<Enter>
> Test
> <Ctrl-D>
> End of input!
> $ ./echo3 | perl -e 'close STDIN'
> Test<Enter>
> Broken pipe!
> $
You might wonder why ('>->') returns an 'Effect' that we have to run instead
of directly returning an action in the base monad. This is because you can
connect things other than 'Producer's and 'Consumer's, like 'Pipe's, which
are effectful stream transformations.
A 'Pipe' is a monad transformer that is a mix between a 'Producer' and
'Consumer', because a 'Pipe' can both 'await' and 'yield'. The following
example 'Pipe' is analogous to the Prelude's 'take', only allowing a fixed
number of values to flow through:
> -- take.hs
>
> import Control.Monad (replicateM_)
> import Pipes
> import Prelude hiding (take)
>
> -- +--------- A 'Pipe' that
> -- | +---- 'await's 'a's and
> -- | | +-- 'yield's 'a's
> -- | | |
> -- v v v
> take :: Int -> Pipe a a IO ()
> take n = do
> replicateM_ n $ do -- Repeat this block 'n' times
> x <- await -- 'await' a value of type 'a'
> yield x -- 'yield' a value of type 'a'
> lift $ putStrLn "You shall not pass!" -- Fly, you fools!
You can use 'Pipe's to transform 'Producer's, 'Consumer's, or even other
'Pipe's using the same ('>->') operator:
@
('>->') :: 'Monad' m => 'Producer' a m r -> 'Pipe' a b m r -> 'Producer' b m r
('>->') :: 'Monad' m => 'Pipe' a b m r -> 'Consumer' b m r -> 'Consumer' a m r
('>->') :: 'Monad' m => 'Pipe' a b m r -> 'Pipe' b c m r -> 'Pipe' a c m r
@
For example, you can compose 'P.take' after 'P.stdinLn' to limit the number
of lines drawn from standard input:
> maxInput :: Int -> Producer String IO ()
> maxInput n = P.stdinLn >-> take n
>>> runEffect $ maxInput 3 >-> P.stdoutLn
Test<Enter>
Test
ABC<Enter>
ABC
42<Enter>
42
You shall not pass!
>>>
... or you can pre-compose 'P.take' before 'P.stdoutLn' to limit the number
of lines written to standard output:
> maxOutput :: Int -> Consumer String IO ()
> maxOutput n = take n >-> P.stdoutLn
>>> runEffect $ P.stdinLn >-> maxOutput 3
<Exact same behavior>
Those both gave the same behavior because ('>->') is associative:
> (p1 >-> p2) >-> p3 = p1 >-> (p2 >-> p3)
Therefore we can just leave out the parentheses:
>>> runEffect $ P.stdinLn >-> take 3 >-> P.stdoutLn
<Exact same behavior>
('>->') is designed to behave like the Unix pipe operator, except with less
quirks. In fact, we can continue the analogy to Unix by defining 'cat'
(named after the Unix @cat@ utility), which reforwards elements endlessly:
> cat :: Monad m => Pipe a a m r
> cat = forever $ do
> x <- await
> yield x
'cat' is the identity of ('>->'), meaning that 'cat' satisfies the
following two laws:
> -- Useless use of 'cat'
> cat >-> p = p
>
> -- Forwarding output to 'cat' does nothing
> p >-> cat = p
Therefore, ('>->') and 'cat' form a 'Category', specifically the category of
Unix pipes, and 'Pipe's are also composable.
A lot of Unix tools have very simple definitions when written using @pipes@:
> -- unix.hs
>
> import Control.Monad (forever)
> import Pipes
> import qualified Pipes.Prelude as P -- Pipes.Prelude provides 'take', too
> import Prelude hiding (head)
>
> head :: Monad m => Int -> Pipe a a m ()
> head = P.take
>
> yes :: Monad m => Producer String m r
> yes = forever $ yield "y"
>
> main = runEffect $ yes >-> head 3 >-> P.stdoutLn
This prints out 3 \'@y@\'s, just like the equivalent Unix pipeline:
> $ ./unix
> y
> y
> y
> $ yes | head -3
> y
> y
> y
> $
This lets us write \"Haskell pipes\" instead of Unix pipes. These are much
easier to build than Unix pipes and we can connect them directly within
Haskell for interoperability with the Haskell language and ecosystem.
-}
{- $listT
@pipes@ also provides a \"ListT done right\" implementation. This differs
from the implementation in @transformers@ because this 'ListT':
* obeys the monad laws, and
* streams data immediately instead of collecting all results into memory.
The latter property is actually an elegant consequence of obeying the monad
laws.
To bind a list within a 'ListT' computation, combine 'Select' and 'each':
> import Pipes
>
> pair :: ListT IO (Int, Int)
> pair = do
> x <- Select $ each [1, 2]
> lift $ putStrLn $ "x = " ++ show x
> y <- Select $ each [3, 4]
> lift $ putStrLn $ "y = " ++ show y
> return (x, y)
You can then loop over a 'ListT' by using 'every':
@
'every' :: 'Monad' m => 'ListT' m a -> 'Producer' a m ()
@
So you can use your 'ListT' within a 'for' loop:
>>> runEffect $ for (every pair) (lift . print)
x = 1
y = 3
(1,3)
y = 4
(1,4)
x = 2
y = 3
(2,3)
y = 4
(2,4)
... or a pipeline:
>>> import qualified Pipes.Prelude as P
>>> runEffect $ every pair >-> P.print
<Exact same behavior>
Note that 'ListT' is lazy and only produces as many elements as we request:
>>> runEffect $ for (every pair >-> P.take 2) (lift . print)
x = 1
y = 3
(1,3)
y = 4
(1,4)
You can also go the other way, binding 'Producer's directly within a
'ListT'. In fact, this is actually what 'Select' was already doing:
@
'Select' :: 'Producer' a m () -> 'ListT' m a
@
This lets you write crazy code like:
> import Pipes
> import qualified Pipes.Prelude as P
>
> input :: Producer String IO ()
> input = P.stdinLn >-> P.takeWhile (/= "quit")
>
> name :: ListT IO String
> name = do
> firstName <- Select input
> lastName <- Select input
> return (firstName ++ " " ++ lastName)
Here we're binding standard input non-deterministically (twice) as if it
were an effectful list:
>>> runEffect $ every name >-> P.stdoutLn
Daniel<Enter>
Fischer<Enter>
Daniel Fischer
Wagner<Enter>
Daniel Wagner
quit<Enter>
Donald<Enter>
Stewart<Enter>
Donald Stewart
Duck<Enter>
Donald Duck
quit<Enter>
quit<Enter>
>>>
Notice how this streams out values immediately as they are generated, rather
than building up a large intermediate result and then printing all the
values in one batch at the end.
`ListT` computations can be combined in more ways than `Pipe`s, so try to
program in `ListT` as much as possible and defer converting it to a `Pipe`
as late as possible using `P.loop`.
You can combine `ListT` computations even if their inputs and outputs are
completely different:
> data In
> = InA A
> | InB B
> | InC C
>
> data Out
> = OutD D
> | OutE E
> | OutF F
>
> -- Independent computations
>
> example1 :: A -> ListT IO D
> example2 :: B -> ListT IO E
> example3 :: C -> ListT IO F
>
> -- Combined computation
>
> total :: In -> ListT IO Out
> total input = case input of
> InA a -> fmap OutD (example1 a)
> InB b -> fmap OutE (example2 b)
> InC c -> fmap OutF (example3 c)
Sometimes you have multiple computations that handle different inputs but
the same output, in which case you don't need to unify their outputs:
> -- Overlapping outputs
>
> example1 :: A -> ListT IO Out
> example2 :: B -> ListT IO Out
> example3 :: C -> ListT IO Out
>
> -- Combined computation
>
> total :: In -> ListT IO Out
> total input = case input of
> InA a -> example1 a
> InB b -> example2 b
> InC c -> example3 c
Other times you have multiple computations that handle the same input but
produce different outputs. You can unify their outputs using the `Monoid`
and `Functor` instances for `ListT`:
> -- Overlapping inputs
>
> example1 :: In -> ListT IO D
> example2 :: In -> ListT IO E
> example3 :: In -> ListT IO F
>
> -- Combined computation
>
> total :: In -> ListT IO Out
> total input =
> fmap OutD (example1 input)
> <> fmap OutE (example2 input)
> <> fmap OutF (example3 input)
You can also chain `ListT` computations, feeding the output of the first
computation as the input to the next computation:
> -- End-to-end
>
> aToB :: A -> ListT IO B
> bToC :: B -> ListT IO C
>
> -- Combined computation
>
> aToC :: A -> LIstT IO C
> aToC = aToB >=> bToC
... or you can just use @do@ notation if you prefer.
However, the `Pipe` type is more general than `ListT` and can represent
things like termination. Therefore you should consider mixing `Pipe`s with
`ListT` when you need to take advantage of these extra features:
> -- Mix ListT with Pipes
>
> example :: In -> ListT IO Out
>
> pipe :: Pipe In Out IO ()
> pipe = Pipes.takeWhile (not . isC) >-> loop example
> where
> isC (InC _) = True
> isC _ = False
So promote your `ListT` logic to a `Pipe` when you need to take advantage of
these `Pipe`-specific features.
-}
{- $tricks
@pipes@ is more powerful than meets the eye so this section presents some
non-obvious tricks you may find useful.
Many pipe combinators will work on unusual pipe types and the next few
examples will use the 'cat' pipe to demonstrate this.
For example, you can loop over the output of a 'Pipe' using 'for', which is
how 'P.map' is defined:
> map :: Monad m => (a -> b) -> Pipe a b m r
> map f = for cat $ \x -> yield (f x)
>
> -- Read this as: For all values flowing downstream, apply 'f'
This is equivalent to:
> map f = forever $ do
> x <- await
> yield (f x)
You can also feed a 'Pipe' input using ('>~'). This means we could have
instead defined the @yes@ pipe like this:
> yes :: Monad m => Producer String m r
> yes = return "y" >~ cat
>
> -- Read this as: Keep feeding "y" downstream
This is equivalent to:
> yes = forever $ yield "y"
You can also sequence two 'Pipe's together. This is how 'P.drop' is
defined:
> drop :: Monad m => Int -> Pipe a a m r
> drop n = do
> replicateM_ n await
> cat
This is equivalent to:
> drop n = do
> replicateM_ n await
> forever $ do
> x <- await
> yield x
You can even compose pipes inside of another pipe:
> customerService :: Producer String IO ()
> customerService = do
> each [ "Hello, how can I help you?" -- Begin with a script
> , "Hold for one second."
> ]
> P.stdinLn >-> P.takeWhile (/= "Goodbye!") -- Now continue with a human
Also, you can often use 'each' in conjunction with ('~>') to traverse nested
data structures. For example, you can print all non-'Nothing' elements
from a doubly-nested list:
>>> runEffect $ (each ~> each ~> each ~> lift . print) [[Just 1, Nothing], [Just 2, Just 3]]
1
2
3
Another neat thing to know is that 'every' has a more general type:
@
'every' :: ('Monad' m, 'Enumerable' t) => t m a -> 'Producer' a m ()
@
'Enumerable' generalizes 'Foldable' and if you have an effectful container
of your own that you want others to traverse using @pipes@, just have your
container implement the 'toListT' method of the 'Enumerable' class:
> class Enumerable t where
> toListT :: Monad m => t m a -> ListT m a
You can even use 'Enumerable' to traverse effectful types that are not even
proper containers, like 'Control.Monad.Trans.Maybe.MaybeT':
> input :: MaybeT IO String
> input = do
> str <- lift getLine
> guard (str /= "Fail")
> return str
>>> runEffect $ every input >-> P.stdoutLn
Test<Enter>
Test
>>> runEffect $ every input >-> P.stdoutLn
Fail<Enter>
>>>
-}
{- $conclusion
This tutorial covers the concepts of connecting, building, and reading
@pipes@ code. However, this library is only the core component in an
ecosystem of streaming components. Derived libraries that build immediately
upon @pipes@ include:
* @pipes-concurrency@: Concurrent reactive programming and message passing
* @pipes-parse@: Minimal utilities for stream parsing
* @pipes-safe@: Resource management and exception safety for @pipes@
* @pipes-group@: Grouping streams in constant space
These libraries provide functionality specialized to common streaming
domains. Additionally, there are several libraries on Hackage that provide
even higher-level functionality, which you can find by searching under the
\"Pipes\" category or by looking for packages with a @pipes-@ prefix in
their name. Current examples include:
* @pipes-extras@: Miscellaneous utilities
* @pipes-network@/@pipes-network-tls@: Networking
* @pipes-zlib@: Compression and decompression
* @pipes-binary@: Binary serialization
* @pipes-attoparsec@: High-performance parsing
* @pipes-aeson@: JSON serialization and deserialization
Even these derived packages still do not explore the full potential of
@pipes@ functionality, which actually permits bidirectional communication.
Advanced @pipes@ users can explore this library in greater detail by
studying the documentation in the "Pipes.Core" module to learn about the
symmetry of the underlying 'Proxy' type and operators.
To learn more about @pipes@, ask questions, or follow @pipes@ development,
you can subscribe to the @haskell-pipes@ mailing list at:
<https://groups.google.com/forum/#!forum/haskell-pipes>
... or you can mail the list directly at:
<mailto:haskell-pipes@googlegroups.com>
Additionally, for questions regarding types or type errors, you might find
the following appendix on types very useful.
-}
{- $types
@pipes@ uses parametric polymorphism (i.e. generics) to overload all
operations. You've probably noticed this overloading already:
* 'yield' works within both 'Producer's and 'Pipe's
* 'await' works within both 'Consumer's and 'Pipe's
* ('>->') connects 'Producer's, 'Consumer's, and 'Pipe's in varying ways
This overloading is great when it works, but when connections fail they
produce type errors that appear intimidating at first. This section
explains the underlying types so that you can work through type errors
intelligently.
'Producer's, 'Consumer's, 'Pipe's, and 'Effect's are all special cases of a
single underlying type: a 'Proxy'. This overarching type permits fully
bidirectional communication on both an upstream and downstream interface.
You can think of it as having the following shape:
> Proxy a' a b' b m r
>
> Upstream | Downstream
> +---------+
> | |
> a' <== <== b' -- Information flowing upstream
> | |
> a ==> ==> b -- Information flowing downstream
> | | |
> +----|----+
> v
> r
The four core types do not use the upstream flow of information. This means
that the @a'@ and @b'@ in the above diagram go unused unless you use the
more advanced features provided in "Pipes.Core".
@pipes@ uses type synonyms to hide unused inputs or outputs and clean up
type signatures. These type synonyms come in two flavors:
* Concrete type synonyms that explicitly close unused inputs and outputs of
the 'Proxy' type
* Polymorphic type synonyms that don't explicitly close unused inputs or
outputs
The concrete type synonyms use @()@ to close unused inputs and 'X' (the
uninhabited type) to close unused outputs:
* 'Effect': explicitly closes both ends, forbidding 'await's and 'yield's
> type Effect = Proxy X () () X
>
> Upstream | Downstream
> +---------+
> | |
> X <== <== ()
> | |
> () ==> ==> X
> | | |
> +----|----+
> v
> r
* 'Producer': explicitly closes the upstream end, forbidding 'await's
> type Producer b = Proxy X () () b
>
> Upstream | Downstream
> +---------+
> | |
> X <== <== ()
> | |
> () ==> ==> b
> | | |
> +----|----+
> v
> r
* 'Consumer': explicitly closes the downstream end, forbidding 'yield's
gitextract_l6xj46in/
├── .github/
│ └── workflows/
│ └── haskell.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── Setup.hs
├── benchmarks/
│ ├── Common.hs
│ ├── LiftBench.hs
│ └── PreludeBench.hs
├── laws.md
├── nix/
│ └── .gitkeep
├── pipes.cabal
├── release.nix
├── shell.nix
├── src/
│ ├── Pipes/
│ │ ├── Core.hs
│ │ ├── Internal.hs
│ │ ├── Lift.hs
│ │ ├── Prelude.hs
│ │ └── Tutorial.hs
│ └── Pipes.hs
├── stack.yaml
└── tests/
└── Main.hs
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (249K chars).
[
{
"path": ".github/workflows/haskell.yml",
"chars": 883,
"preview": "name: Haskell CI\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n build:\n\n runs-on"
},
{
"path": ".gitignore",
"chars": 63,
"preview": "cabal-dev\n.cabal-sandbox\ncabal.sandbox.config\ndist\n.stack-work\n"
},
{
"path": "CHANGELOG.md",
"chars": 3356,
"preview": "4.3.16\n\n* Fix example code for `every`\n* Improved documentation for `ListT`\n\n4.3.15\n\n* Build against `ghc-9.0`\n\n4.3.14\n\n"
},
{
"path": "LICENSE",
"chars": 1528,
"preview": "Copyright (c) 2012-2016 Gabriella Gonzalez\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with"
},
{
"path": "README.md",
"chars": 4592,
"preview": "# `pipes`\n\n`pipes` is a clean and powerful stream processing library that lets you build\nand connect reusable streaming "
},
{
"path": "Setup.hs",
"chars": 46,
"preview": "import Distribution.Simple\nmain = defaultMain\n"
},
{
"path": "benchmarks/Common.hs",
"chars": 777,
"preview": "module Common (commonMain) where\n\nimport Criterion.Main (Benchmark, runMode)\nimport Criterion.Main.Options as Criterion\n"
},
{
"path": "benchmarks/LiftBench.hs",
"chars": 1990,
"preview": "{-# LANGUAGE RankNTypes #-}\nmodule Main (main) where\n\nimport Common (commonMain)\nimport Control.Monad.Identity\nimport qu"
},
{
"path": "benchmarks/PreludeBench.hs",
"chars": 3220,
"preview": "{-# LANGUAGE RankNTypes #-}\nmodule Main (main) where\n\nimport Criterion.Main\nimport Common (commonMain)\nimport Control.Mo"
},
{
"path": "laws.md",
"chars": 68393,
"preview": "# Kleisli Category\n\n Define:\n \n return\n :: (Monad m)\n => r -> Proxy a' a b' b m r\n return = P"
},
{
"path": "nix/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "pipes.cabal",
"chars": 3753,
"preview": "Name: pipes\nVersion: 4.3.16\nCabal-Version: >= 1.10\nBuild-Type: Simple\nTested-With: GHC == 7.10.3, GHC == 8.0.2, GHC == 8"
},
{
"path": "release.nix",
"chars": 844,
"preview": "let\n overlay = pkgsNew: pkgsOld: {\n haskellPackages = pkgsOld.haskellPackages.override (old: {\n overrides =\n "
},
{
"path": "shell.nix",
"chars": 33,
"preview": "(import ./release.nix).pipes.env\n"
},
{
"path": "src/Pipes/Core.hs",
"chars": 22987,
"preview": "{-| The core functionality for the 'Proxy' monad transformer\n\n Read \"Pipes.Tutorial\" if you want a beginners tutorial"
},
{
"path": "src/Pipes/Internal.hs",
"chars": 9538,
"preview": "{-| This is an internal module, meaning that it is unsafe to import unless you\n understand the risks.\n\n This modul"
},
{
"path": "src/Pipes/Lift.hs",
"chars": 10168,
"preview": "{-# LANGUAGE CPP #-}\n\n{-| Many actions in base monad transformers cannot be automatically\n 'Control.Monad.Trans.Class"
},
{
"path": "src/Pipes/Prelude.hs",
"chars": 24938,
"preview": "{-| General purpose utilities\n\n The names in this module clash heavily with the Haskell Prelude, so I\n recommend t"
},
{
"path": "src/Pipes/Tutorial.hs",
"chars": 51249,
"preview": "{-# OPTIONS_GHC -fno-warn-unused-imports #-}\n\n{-| Conventional Haskell stream programming forces you to choose only two "
},
{
"path": "src/Pipes.hs",
"chars": 21819,
"preview": "{-# LANGUAGE CPP #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-#"
},
{
"path": "stack.yaml",
"chars": 19,
"preview": "resolver: lts-6.13\n"
},
{
"path": "tests/Main.hs",
"chars": 7653,
"preview": "module Main (main) where\n\nimport Data.Function (on)\nimport Data.List ("
}
]
About this extraction
This page contains the full source code of the Gabriel439/Haskell-Pipes-Library GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (232.3 KB), approximately 67.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.