├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Setup.hs ├── list-transformer.cabal ├── release.nix ├── shell.nix ├── src └── List │ └── Transformer.hs ├── stack.yaml └── test └── DocTest.hs /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: haskell 2 | 3 | ghc: 4 | - "8.2" 5 | - "8.0" 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.1.1 2 | 3 | - Support older versions of `base` 4 | 5 | 1.1.0: 6 | 7 | - BREAKING CHANGE: Remove `MonadTrans` instance for `ZipListT` 8 | 9 | 1.0.9: 10 | 11 | - `MFunctor` instances for `ListT` / `Step` 12 | 13 | 1.0.8: 14 | 15 | - Improve documentation 16 | 17 | 1.0.7: 18 | 19 | - Add `ZipListT` 20 | - Add `dropWhile` 21 | 22 | 1.0.6: 23 | 24 | - Build against GHC 8.8 25 | 26 | 1.0.5: 27 | 28 | - Disable `-Wcompat` build flag for GHC < 8.0 29 | 30 | 1.0.4: 31 | 32 | - Add `Semigroup` instance 33 | - Make package `-Wcompat`-clean 34 | 35 | 1.0.3: 36 | 37 | - Fix typos in tutorial 38 | 39 | 1.0.2: 40 | 41 | - Add `takeWhile` 42 | - Add `MonadFail` instance for `ListT` 43 | 44 | 1.0.1: 45 | 46 | - Add `take` / `drop` / `unfold` / `zip` 47 | 48 | 1.0.0: 49 | 50 | - Initial release 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Gabriella Gonzalez (c) 2016 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Gabriella Gonzalez nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `list-transformer` 2 | 3 | This library provides a "`ListT` done right implementation" that obeys the 4 | `Monad` laws. 5 | 6 | Here is an example of how you build and consume `ListT` values: 7 | 8 | ```haskell 9 | import List.Transformer 10 | 11 | import qualified System.IO 12 | 13 | stdin :: ListT IO String 14 | stdin = ListT (do 15 | eof <- System.IO.isEOF 16 | if eof 17 | then return Nil 18 | else do 19 | string <- getLine 20 | return (Cons string stdin) ) 21 | 22 | stdout :: ListT IO String -> IO () 23 | stdout strings = do 24 | s <- next strings 25 | case s of 26 | Nil -> return () 27 | Cons string strings' -> do 28 | putStrLn string 29 | stdout strings' 30 | 31 | main :: IO () 32 | main = stdout stdin 33 | ``` 34 | 35 | ... and here's an example use of this library to print every natural number 36 | using a style resembling list comprehensions: 37 | 38 | ```haskell 39 | import List.Transformer 40 | 41 | main :: IO () 42 | main = runListT (do 43 | n <- select [0..] 44 | liftIO (print n) ) 45 | ``` 46 | 47 | To learn more, read the 48 | [tutorial](http://hackage.haskell.org/package/list-transformer/docs/List-Transformer.html). 49 | 50 | ## Quick start 51 | 52 | Install [the `stack` tool](http://haskellstack.org/) and then run: 53 | 54 | ```bash 55 | $ stack setup 56 | $ stack ghci list-transformer 57 | Prelude> import List.Transformer 58 | Prelude List.Transformer> runListT (do n <- select [0..]; liftIO (print n) ) 59 | 0 60 | 1 61 | 2 62 | 3 63 | ... 64 | ``` 65 | 66 | ## Development Status 67 | 68 | [![Build Status](https://travis-ci.org/Gabriella439/Haskell-List-Transformer-Library.png)](https://travis-ci.org/Gabriella439/Haskell-List-Transformer-Library) 69 | 70 | This library is pretty small so I don't expect there to be breaking changes 71 | since there's not that much that could possibly change. 72 | 73 | ## License (BSD 3-clause) 74 | 75 | Copyright (c) 2016 Gabriella Gonzalez 76 | All rights reserved. 77 | 78 | Redistribution and use in source and binary forms, with or without modification, 79 | are permitted provided that the following conditions are met: 80 | 81 | * Redistributions of source code must retain the above copyright notice, this 82 | list of conditions and the following disclaimer. 83 | 84 | * Redistributions in binary form must reproduce the above copyright notice, this 85 | list of conditions and the following disclaimer in the documentation and/or 86 | other materials provided with the distribution. 87 | 88 | * Neither the name of Gabriella Gonzalez nor the names of other contributors may 89 | be used to endorse or promote products derived from this software without 90 | specific prior written permission. 91 | 92 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 93 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 94 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 95 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 96 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 97 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 98 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 99 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 100 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 101 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 102 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /list-transformer.cabal: -------------------------------------------------------------------------------- 1 | name: list-transformer 2 | version: 1.1.1 3 | synopsis: List monad transformer 4 | description: This library provides a list monad transformer that 5 | enriches lists with effects and streams efficiently in 6 | constant space. 7 | . 8 | This library also has an extensive tutorial in the 9 | "List.Transformer" module which explains the motivation 10 | behind this type and how to use the type fluently. 11 | homepage: https://github.com/Gabriella439/Haskell-List-Transformer-Library 12 | license: BSD3 13 | license-file: LICENSE 14 | author: Gabriella Gonzalez 15 | maintainer: GenuineGabriella@gmail.com 16 | copyright: 2016 Gabriella Gonzalez 17 | category: Control 18 | build-type: Simple 19 | cabal-version: >=1.10 20 | extra-source-files: CHANGELOG.md 21 | 22 | library 23 | hs-source-dirs: src 24 | exposed-modules: List.Transformer 25 | default-language: Haskell2010 26 | build-depends: base >= 4.5 && < 5 27 | , mtl >= 2.1 && < 2.4 28 | , mmorph >= 1.1.3 && < 1.3 29 | if !impl(ghc >= 8.0) 30 | build-depends: semigroups == 0.18.* 31 | if !impl(ghc >= 8.0) 32 | ghc-options: -Wall 33 | if impl(ghc >= 8.0) 34 | ghc-options: -Wall -Wcompat 35 | 36 | test-suite doctest 37 | type: exitcode-stdio-1.0 38 | main-is: DocTest.hs 39 | hs-source-dirs: test 40 | build-depends: 41 | base 42 | , doctest 43 | , list-transformer 44 | default-language: Haskell2010 45 | -------------------------------------------------------------------------------- /release.nix: -------------------------------------------------------------------------------- 1 | let 2 | config = { 3 | packageOverrides = pkgs: { 4 | haskellPackages = pkgs.haskellPackages.override { 5 | overrides = pkgs.haskell.lib.packageSourceOverrides { 6 | list-transformer = ./.; 7 | }; 8 | }; 9 | }; 10 | }; 11 | 12 | pkgs = 13 | import { inherit config; }; 14 | 15 | in 16 | { inherit (pkgs.haskellPackages) list-transformer; 17 | } 18 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import ./release.nix).list-transformer.env 2 | -------------------------------------------------------------------------------- /src/List/Transformer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE CPP #-} 3 | {-# LANGUAGE DeriveFoldable #-} 4 | {-# LANGUAGE DeriveTraversable #-} 5 | {-# LANGUAGE FlexibleInstances #-} 6 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE UndecidableInstances #-} 9 | 10 | -- | The `ListT` type is like a list that lets you interleave effects between 11 | -- each element of the list. 12 | module List.Transformer 13 | ( 14 | -- * Introduction 15 | -- $intro 16 | 17 | -- ** Example: stdin, stdout 18 | -- $standardStreams 19 | 20 | -- ** Core operations 21 | -- $core 22 | 23 | -- ** Monadic combination 24 | -- $monad 25 | 26 | -- ** Exercise: Interaction 27 | -- $interaction 28 | 29 | -- * ListT 30 | ListT(..) 31 | 32 | -- ** Consuming 33 | -- $pleaseStream 34 | , runListT 35 | , fold 36 | , foldM 37 | 38 | -- ** Constructing 39 | -- $constructing 40 | , select 41 | , unfold 42 | 43 | -- ** Removing elements 44 | , take 45 | , drop 46 | , dropWhile 47 | , takeWhile 48 | -- $filter 49 | 50 | -- ** Concatenation 51 | -- $concatenation 52 | 53 | -- ** Pairwise combination 54 | -- $pairwise 55 | , zip 56 | 57 | -- ** Repetition 58 | -- $repetition 59 | 60 | -- * Step 61 | , Step(..) 62 | 63 | -- * Alternative instances 64 | , ZipListT(..) 65 | 66 | -- * Re-exports 67 | , MonadTrans(..) 68 | , MonadIO(..) 69 | , Alternative(..) 70 | , MFunctor (..) 71 | ) where 72 | 73 | #if MIN_VERSION_base(4,8,0) 74 | import Control.Applicative (Alternative(..), liftA2) 75 | #else 76 | import Control.Applicative (Applicative(..), Alternative(..), liftA2) 77 | import Data.Foldable (Foldable) 78 | import Data.Functor ((<$)) 79 | import Data.Monoid (Monoid(..)) 80 | import Data.Traversable (Traversable) 81 | #endif 82 | import Control.Monad (MonadPlus(..)) 83 | import Control.Monad.Error.Class (MonadError(..)) 84 | #if MIN_VERSION_base(4,9,0) && !(MIN_VERSION_base(4,13,0)) 85 | import Control.Monad.Fail (MonadFail(..)) 86 | #endif 87 | import Control.Monad.Morph (MFunctor (..)) 88 | import Control.Monad.State.Class (MonadState(..)) 89 | import Control.Monad.Reader.Class (MonadReader(..)) 90 | import Control.Monad.Trans (MonadTrans(..), MonadIO(..)) 91 | import Data.Semigroup (Semigroup(..)) 92 | import Prelude hiding (drop, dropWhile, pred, take, takeWhile, zip) 93 | 94 | import qualified Data.Foldable 95 | 96 | -- $setup 97 | -- >>> :set -XNoMonomorphismRestriction 98 | 99 | {- $intro 100 | 101 | The type's definition is very short: 102 | 103 | @newtype 'ListT' m a = ListT { next :: m ('Step' m a) }@ 104 | 105 | Every `ListT` begins with an outermost effect (the @\'m\'@, commonly 'IO'). The return value of that effect is either: 106 | 107 | @data 'Step' m a = Cons a ('ListT' m a) | Nil@ 108 | 109 | * Cons: a new list element followed by the rest of the list 110 | * Nil : an empty list 111 | 112 | -} 113 | 114 | {- $standardStreams 115 | 116 | You most commonly use the ListT when you wish to generate each element of 117 | the list using `IO`. For example, you can read lines from standard input: 118 | 119 | > import List.Transformer 120 | > 121 | > import qualified System.IO 122 | > 123 | > stdin :: ListT IO String 124 | > stdin = ListT (do 125 | > eof <- System.IO.isEOF 126 | > if eof 127 | > then return Nil 128 | > else do 129 | > string <- getLine 130 | > return (Cons string stdin) ) 131 | 132 | You can also loop over a `ListT` to consume elements one-at-a-time. You 133 | \"pay as you go\" for effects, only running what you actually need: 134 | 135 | > stdout :: ListT IO String -> IO () 136 | > stdout strings = do 137 | > s <- next strings 138 | > case s of 139 | > Nil -> return () 140 | > Cons string strings' -> do 141 | > putStrLn string 142 | > stdout strings' 143 | 144 | Combining @stdin@ and @stdout@ forwards lines one-by-one from standard input 145 | to standard output: 146 | 147 | > main :: IO () 148 | > main = stdout stdin 149 | 150 | These lines stream in constant space, never retaining more than one line in 151 | memory: 152 | 153 | > $ runghc aboveExample.hs 154 | > Test 155 | > Test 156 | > 123 157 | > 123 158 | > ABC 159 | > ABC 160 | > 161 | > $ 162 | 163 | -} 164 | 165 | {- $core 166 | 167 | The most important operations that you should familiarize yourself with are: 168 | 169 | * `empty`, which gives you an empty `ListT` with 0 elements 170 | 171 | > empty :: ListT IO a 172 | 173 | * `pure` / `return`, which both convert a value into a one-element `ListT` 174 | 175 | > pure, return :: a -> ListT IO a 176 | 177 | * `liftIO`, which converts an `IO` action into a one-element `ListT` 178 | 179 | > liftIO :: IO a -> ListT IO a 180 | 181 | * (`<|>`), which concatenates two `ListT`s 182 | 183 | > (<|>) :: ListT IO a -> ListT IO a -> ListT IO a 184 | 185 | * (`>>=`), which powers @do@ notation and @MonadComprehensions@ 186 | 187 | > (>>=) :: ListT IO a -> (a -> ListT IO b) -> ListT IO b 188 | 189 | * `select`, which converts a plain list into a `ListT` 190 | 191 | > select :: [a] -> ListT IO a 192 | 193 | -} 194 | 195 | {- $monad 196 | 197 | Sometimes we can simplify the code by taking advantage of the fact that the 198 | `Monad` instance for `ListT` behaves like a list comprehension: 199 | 200 | > stdout :: ListT IO String -> IO () 201 | > stdout strings = runListT (do 202 | > string <- strings 203 | > liftIO (putStrLn string) ) 204 | 205 | You can read the above code as saying: \"for each @string@ in @strings@, 206 | call `putStrLn` on @string@." 207 | 208 | You can even use list comprehension syntax if you enable the 209 | @MonadComprehensions@ language extension: 210 | 211 | > stdout strings = runListT [ r | str <- strings, r <- liftIO (putStrLn str) ] 212 | 213 | There are a few ways we could consider defining a `ListT` analogue to the `mapM` 214 | function from `Prelude`, but none are given in this library because they need 215 | require only (`>>=`) and some trivial lifting. 216 | 217 | > mapM :: (a -> IO b) -> [a] -> IO [b] 218 | > ( \f xs -> xs >>= f ) :: (a -> ListT IO b) -> ListT IO a -> ListT IO b 219 | > ( \f xs -> select xs >>= lift . f ) :: (a -> IO b) -> [a] -> ListT IO b 220 | > ( \f xs -> xs >>= lift . f ) :: (a -> IO b) -> ListT IO a -> ListT IO b 221 | 222 | A critical difference between `mapM` and `ListT`'s monad is that `ListT` will 223 | stream in constant space, whereas `mapM` buffers the entire output list before 224 | returning a single element. 225 | 226 | -} 227 | 228 | {- $interaction 229 | 230 | To test your understanding, guess what this code does and then test your 231 | guess by running the code: 232 | 233 | @ 234 | import List.Transformer ('ListT', 'runListT', 'liftIO', ('<|>'), 'select') 235 | import Data.Foldable ('Data.Foldable.asum') 236 | import Data.List ('Data.List.repeat') 237 | 238 | strings :: 'ListT' IO String 239 | strings = do 240 | 'select' ('Data.List.repeat' ()) 241 | 'Data.Foldable.asum' 242 | [ pure "" 243 | , pure "Say something:" 244 | , do 245 | x <- 'liftIO' getLine 246 | return ("You said: " '<|>' x) 247 | ] 248 | 249 | main :: IO () 250 | main = 'runListT' (do 251 | string \<- pure "Hello, there!" '<|>' strings 252 | 'liftIO' (putStrLn string) ) 253 | @ 254 | 255 | -} 256 | 257 | {-| This is like a list except that you can interleave effects between each list 258 | element. For example: 259 | 260 | > stdin :: ListT IO String 261 | > stdin = ListT (do 262 | > eof <- System.IO.isEOF 263 | > if eof 264 | > then return Nil 265 | > else do 266 | > line <- getLine 267 | > return (Cons line stdin) ) 268 | 269 | The mnemonic is \"List Transformer\" because this type takes a base `Monad`, 270 | @\'m\'@, and returns a new transformed `Monad` that adds support for 271 | list comprehensions 272 | -} 273 | newtype ListT m a = ListT { next :: m (Step m a) } 274 | deriving (Foldable, Traversable) 275 | 276 | instance MonadTrans ListT where 277 | lift m = ListT (do 278 | x <- m 279 | return (Cons x empty) ) 280 | 281 | instance Monad m => Functor (ListT m) where 282 | fmap k (ListT m) = ListT (do 283 | s <- m 284 | return (fmap k s) ) 285 | 286 | instance Monad m => Applicative (ListT m) where 287 | pure x = ListT (return (Cons x empty)) 288 | 289 | ListT m <*> l = ListT (do 290 | s <- m 291 | case s of 292 | Nil -> return Nil 293 | Cons f l' -> next (fmap f l <|> (l' <*> l)) ) 294 | 295 | ListT m *> l = ListT (do 296 | s <- m 297 | case s of 298 | Nil -> return Nil 299 | Cons _ l' -> next (l <|> (l' *> l)) ) 300 | 301 | ListT m <* l = ListT (do 302 | s <- m 303 | case s of 304 | Nil -> return Nil 305 | Cons x l' -> next ((x <$ l) <|> (l' <* l)) ) 306 | 307 | instance Monad m => Monad (ListT m) where 308 | return = pure 309 | 310 | ListT m >>= k = ListT (do 311 | s <- m 312 | case s of 313 | Nil -> return Nil 314 | Cons x l' -> next (k x <|> (l' >>= k)) ) 315 | 316 | #if !(MIN_VERSION_base(4,13,0)) 317 | fail _ = mzero 318 | #endif 319 | 320 | instance Monad m => Alternative (ListT m) where 321 | empty = ListT (return Nil) 322 | 323 | ListT m <|> l = ListT (do 324 | s <- m 325 | case s of 326 | Nil -> next l 327 | Cons x l' -> return (Cons x (l' <|> l)) ) 328 | 329 | instance Monad m => MonadPlus (ListT m) where 330 | mzero = empty 331 | 332 | mplus = (<|>) 333 | 334 | #if MIN_VERSION_base(4,9,0) 335 | instance Monad m => MonadFail (ListT m) where 336 | fail _ = mzero 337 | #endif 338 | 339 | instance (Monad m, Data.Semigroup.Semigroup a) => Data.Semigroup.Semigroup (ListT m a) where 340 | (<>) = liftA2 (<>) 341 | 342 | instance (Monad m, Data.Semigroup.Semigroup a, Monoid a) => Monoid (ListT m a) where 343 | mempty = pure mempty 344 | 345 | #if !(MIN_VERSION_base(4,11,0)) 346 | mappend = (<>) 347 | #endif 348 | 349 | instance MonadIO m => MonadIO (ListT m) where 350 | liftIO m = lift (liftIO m) 351 | 352 | instance MonadError e m => MonadError e (ListT m) where 353 | throwError e = ListT (throwError e) 354 | 355 | catchError (ListT m) k = ListT (catchError m (next . k)) 356 | 357 | instance MonadReader i m => MonadReader i (ListT m) where 358 | ask = lift ask 359 | 360 | local k (ListT m) = ListT (do 361 | s <- local k m 362 | case s of 363 | Nil -> return Nil 364 | Cons x l -> return (Cons x (local k l)) ) 365 | 366 | reader k = lift (reader k) 367 | 368 | instance MonadState s m => MonadState s (ListT m) where 369 | get = lift get 370 | 371 | put x = lift (put x) 372 | 373 | state k = lift (state k) 374 | 375 | instance MFunctor ListT where 376 | #if MIN_VERSION_base(4,8,0) 377 | hoist f xs = ListT (f (fmap (hoist f) (next xs))) 378 | #else 379 | hoist f xs = ListT (f (next xs >>= \x -> return (hoist f x))) 380 | #endif 381 | 382 | instance (Monad m, Num a) => Num (ListT m a) where 383 | fromInteger n = pure (fromInteger n) 384 | 385 | negate = fmap negate 386 | abs = fmap abs 387 | signum = fmap signum 388 | 389 | (+) = liftA2 (+) 390 | (*) = liftA2 (*) 391 | (-) = liftA2 (-) 392 | 393 | instance (Monad m, Fractional a) => Fractional (ListT m a) where 394 | fromRational n = pure (fromRational n) 395 | 396 | recip = fmap recip 397 | 398 | (/) = liftA2 (/) 399 | 400 | instance (Monad m, Floating a) => Floating (ListT m a) where 401 | pi = pure pi 402 | 403 | exp = fmap exp 404 | sqrt = fmap sqrt 405 | log = fmap log 406 | sin = fmap sin 407 | tan = fmap tan 408 | cos = fmap cos 409 | asin = fmap asin 410 | atan = fmap atan 411 | acos = fmap acos 412 | sinh = fmap sinh 413 | tanh = fmap tanh 414 | cosh = fmap cosh 415 | asinh = fmap asinh 416 | atanh = fmap atanh 417 | acosh = fmap acosh 418 | 419 | (**) = liftA2 (**) 420 | logBase = liftA2 logBase 421 | 422 | {-| Use this to drain a `ListT`, running it to completion and discarding all 423 | values. For example: 424 | 425 | > stdout :: ListT IO String -> IO () 426 | > stdout l = runListT (do 427 | > str <- l 428 | > liftIO (putStrLn str) ) 429 | 430 | The most common specialized type for `runListT` will be: 431 | 432 | > runListT :: ListT IO a -> IO () 433 | -} 434 | runListT :: Monad m => ListT m a -> m () 435 | runListT (ListT m) = do 436 | s <- m 437 | case s of 438 | Nil -> return () 439 | Cons _ l' -> runListT l' 440 | 441 | {-| Use this to fold a `ListT` into a single value. This is designed to be 442 | used with the @foldl@ library: 443 | 444 | > import Control.Foldl (purely) 445 | > import List.Transformer (fold) 446 | > 447 | > purely fold :: Monad m => Fold a b -> ListT m a -> m b 448 | 449 | ... but you can also use the `fold` function directly: 450 | 451 | > fold (+) 0 id :: Num a => ListT m a -> m a 452 | 453 | >>> fold (<>) "" id (select ["a", "b", "c", "d", "e"]) 454 | "abcde" 455 | -} 456 | fold :: Monad m => (x -> a -> x) -> x -> (x -> b) -> ListT m a -> m b 457 | fold step begin done l = go begin l 458 | where 459 | go !x (ListT m) = do 460 | s <- m 461 | case s of 462 | Cons a l' -> go (step x a) l' 463 | Nil -> return (done x) 464 | 465 | {-| Use this to fold a `ListT` into a single value. This is designed to be 466 | used with the @foldl@ library: 467 | 468 | > import Control.Foldl (impurely) 469 | > import List.Transformer (fold) 470 | > 471 | > impurely fold :: Monad m => FoldM m a b -> ListT m a -> m b 472 | 473 | ... but you can also use the `foldM` function directly. 474 | -} 475 | foldM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> ListT m a -> m b 476 | foldM step begin done l0 = do 477 | x0 <- begin 478 | go x0 l0 479 | where 480 | go !x (ListT m) = do 481 | s <- m 482 | case s of 483 | Cons a l' -> do 484 | x' <- step x a 485 | go x' l' 486 | Nil -> done x 487 | 488 | {- $pleaseStream 489 | 490 | This library is designed to stream results in constant space and does not 491 | expose an obvious way to collect all the results into memory. As a rule of 492 | thumb if you think you need to collect all the results in memory try to 493 | instead see if you can consume the results as they are being generated (such 494 | as in all the above examples). If you can stream the data from start to 495 | finish then your code will use significantly less memory and your program 496 | will become more responsive. 497 | 498 | -} 499 | 500 | {- $constructing 501 | 502 | `empty` is the empty list with no effects. 503 | 504 | Use `pure`/`return` to construct a singleton list with no effects. Use `liftIO` 505 | to turn an effect into a singleton list whose sole element is the effect's result. 506 | 507 | Suppose you want to build a `ListT` with three elements and no effects. 508 | You could write: 509 | 510 | > pure 1 <|> pure 2 <|> pure 3 :: ListT IO Int 511 | 512 | ... although you would probably prefer to use `select` instead: 513 | 514 | > select [1, 2, 3] :: ListT IO Int 515 | 516 | -} 517 | 518 | {-| Convert any collection that implements `Foldable` to another collection that 519 | implements `Alternative` 520 | 521 | For this library, the most common specialized type for `select` will be: 522 | 523 | > select :: [a] -> ListT IO a 524 | -} 525 | select :: (Foldable f, Alternative m) => f a -> m a 526 | select = Data.Foldable.foldr cons empty 527 | where 528 | cons x xs = pure x <|> xs 529 | 530 | 531 | -- | @take n xs@ takes @n@ elements from the head of @xs@. 532 | -- 533 | -- >>> let list xs = do x <- select xs; liftIO (print (show x)); return x 534 | -- >>> let sum = fold (+) 0 id 535 | -- >>> sum (take 2 (list [5,4,3,2,1])) 536 | -- "5" 537 | -- "4" 538 | -- 9 539 | take :: Monad m => Int -> ListT m a -> ListT m a 540 | take n l 541 | | n <= 0 = empty 542 | | otherwise = ListT (do 543 | s <- next l 544 | case s of 545 | Cons a l' -> return (Cons a (take (n-1) l')) 546 | Nil -> return Nil) 547 | 548 | -- | @drop n xs@ drops @n@ elements from the head of @xs@, but still runs their 549 | -- effects. 550 | -- 551 | -- >>> let list xs = do x <- select xs; liftIO (print (show x)); return x 552 | -- >>> let sum = fold (+) 0 id 553 | -- >>> sum (drop 2 (list [5,4,3,2,1])) 554 | -- "5" 555 | -- "4" 556 | -- "3" 557 | -- "2" 558 | -- "1" 559 | -- 6 560 | drop :: Monad m => Int -> ListT m a -> ListT m a 561 | drop n l 562 | | n <= 0 = l 563 | | otherwise = ListT (do 564 | s <- next l 565 | case s of 566 | Cons _ l' -> next (drop (n-1) l') 567 | Nil -> return Nil) 568 | 569 | -- | @dropWhile pred xs@ drops elements from the head of @xs@ if they 570 | -- satisfy the predicate, but still runs their effects. 571 | -- 572 | -- >>> let list xs = do x <- select xs; liftIO (print (show x)); return x 573 | -- >>> let sum = fold (+) 0 id 574 | -- >>> sum (dropWhile even (list [2,4,5,7,8])) 575 | -- "2" 576 | -- "4" 577 | -- "5" 578 | -- "7" 579 | -- "8" 580 | -- 20 581 | dropWhile :: Monad m => (a -> Bool) -> ListT m a -> ListT m a 582 | dropWhile pred l = ListT (do 583 | n <- next l 584 | case n of 585 | Cons x l' 586 | | pred x -> next (dropWhile pred l') 587 | | otherwise -> return (Cons x l') 588 | Nil -> return Nil ) 589 | 590 | -- | @takeWhile pred xs@ takes elements from @xs@ until the predicate @pred@ fails 591 | -- 592 | -- >>> let list xs = do x <- select xs; liftIO (print (show x)); return x 593 | -- >>> let sum = fold (+) 0 id 594 | -- >>> sum (takeWhile even (list [2,4,5,7,8])) 595 | -- "2" 596 | -- "4" 597 | -- "5" 598 | -- 6 599 | takeWhile :: Monad m => (a -> Bool) -> ListT m a -> ListT m a 600 | takeWhile pred l = ListT (do 601 | n <- next l 602 | case n of 603 | Cons x l' | pred x -> return (Cons x (takeWhile pred l')) 604 | _ -> return Nil ) 605 | 606 | {- $filter 607 | 608 | To filter elements from a list based on a predicate, use `Control.Monad.guard`. 609 | For example, the following function is analogous to `Data.List.filter`: 610 | 611 | > filter :: Monad m => (a -> m Bool) -> ListT m a -> ListT m a 612 | > filter pred as = do 613 | > a <- as 614 | > b <- lift (pred a) 615 | > guard b 616 | > return a 617 | 618 | -} 619 | 620 | {- $concatenation 621 | 622 | Use (`<|>`) to concatenate two lists. 623 | 624 | > (<|>) :: ListT IO a -> ListT IO a -> ListT IO a 625 | 626 | Use `Data.Foldable.asum` to flatten a list of lists. 627 | 628 | > asum :: [ListT IO a] -> ListT IO a 629 | 630 | Use `Control.Monad.join` to flatten a `ListT` of `ListT`s. 631 | 632 | > join :: ListT IO (ListT IO a) -> ListT IO a 633 | 634 | -} 635 | 636 | {- $pairwise 637 | 638 | The (`<>`) operation joins every combination of an element from one list with 639 | an element from the other. 640 | 641 | >>> runListT ( (select ["a", "b"] <> select ["1", "2", "3"]) >>= (liftIO . print) ) 642 | "a1" 643 | "a2" 644 | "a3" 645 | "b1" 646 | "b2" 647 | "b3" 648 | 649 | This is the same combinatorial effect that (`>>=`) produces. 650 | 651 | >>> runListT (do x <- select ["a", "b"]; y <- select ["1", "2", "3"]; liftIO (print (x <> y))) 652 | "a1" 653 | "a2" 654 | "a3" 655 | "b1" 656 | "b2" 657 | "b3" 658 | 659 | -} 660 | 661 | {- $repetition 662 | 663 | Unbounded repetition can be induced using @'select' ('Data.List.repeat' ())@. 664 | For example, here are several functions analogous to 'Data.List.cycle': 665 | 666 | > cycle1 :: Monad m => a -> ListT m a 667 | > cycle1 a = do 668 | > select (Data.List.repeat ()) 669 | > return a 670 | 671 | > cycle2 :: Monad m => [a] -> ListT m a 672 | > cycle2 as = do 673 | > select (Data.List.repeat ()) 674 | > select as 675 | 676 | > cycle3 :: Monad m => m a -> ListT m a 677 | > cycle3 m = do 678 | > select (Data.List.repeat ()) 679 | > lift m 680 | 681 | > cycle4 :: Monad m => [m a] -> ListT m a 682 | > cycle4 ms = do 683 | > select (Data.List.repeat ()) 684 | > m <- select ms 685 | > lift m 686 | 687 | > cycle5 :: Monad m => ListT m a -> ListT m a 688 | > cycle5 x = do 689 | > select (Data.List.repeat ()) 690 | > x 691 | 692 | > cycle6 :: Monad m => [ListT m a] -> ListT m a 693 | > cycle6 lists = do 694 | > select (Data.List.repeat ()) 695 | > x <- select lists 696 | > x 697 | 698 | In a similar manner, we can use 'Data.List.replicate' as the initial selection 699 | to achieve bounded repetition: 700 | 701 | > replicate1 :: Monad m => Int -> a -> ListT m a 702 | > replicate1 n a = do 703 | > select (Data.List.replicate n ()) 704 | > return a 705 | 706 | > replicate2 :: Monad m => Int -> [a] -> ListT m a 707 | > replicate2 n as = do 708 | > select (Data.List.replicate n ()) 709 | > select as 710 | 711 | > replicate3 :: Monad m => Int -> m a -> ListT m a 712 | > replicate3 n m = do 713 | > select (Data.List.replicate n ()) 714 | > lift m 715 | 716 | > replicate4 :: Monad m => Int -> [m a] -> ListT m a 717 | > replicate4 n ms = do 718 | > select (Data.List.replicate n ()) 719 | > m <- select ms 720 | > lift m 721 | 722 | > replicate5 :: Monad m => Int -> ListT m a -> ListT m a 723 | > replicate5 n x = do 724 | > select (Data.List.replicate n ()) 725 | > x 726 | 727 | > replicate6 :: Monad m => Int -> [ListT m a] -> ListT m a 728 | > replicate6 n lists = do 729 | > select (Data.List.replicate n ()) 730 | > x <- select lists 731 | > x 732 | 733 | -} 734 | 735 | -- | @unfold step seed@ generates a 'ListT' from a @step@ function and an 736 | -- initial @seed@. 737 | unfold :: Monad m => (b -> m (Maybe (a, b))) -> b -> ListT m a 738 | unfold step = loop 739 | where 740 | loop seed = ListT (do 741 | mx <- step seed 742 | case mx of 743 | Just (x, seed') -> return (Cons x (loop seed')) 744 | Nothing -> return Nil) 745 | 746 | -- | @zip xs ys@ zips two 'ListT' together, running the effects of each before 747 | -- possibly recursing. Notice in the example below, @4@ is output even though 748 | -- it has no corresponding element in the second list. 749 | -- 750 | -- >>> let list xs = do x <- select xs; liftIO (print (show x)); return x 751 | -- >>> runListT (zip (list [1,2,3,4,5]) (list [6,7,8])) 752 | -- "1" 753 | -- "6" 754 | -- "2" 755 | -- "7" 756 | -- "3" 757 | -- "8" 758 | -- "4" 759 | zip :: Monad m => ListT m a -> ListT m b -> ListT m (a, b) 760 | zip xs ys = ListT (do 761 | sx <- next xs 762 | sy <- next ys 763 | case (sx, sy) of 764 | (Cons x xs', Cons y ys') -> return (Cons (x, y) (zip xs' ys')) 765 | _ -> return Nil) 766 | 767 | 768 | {-| Pattern match on this type when you loop explicitly over a `ListT` using 769 | `next`. For example: 770 | 771 | > stdout :: ListT IO String -> IO () 772 | > stdout l = do 773 | > s <- next l 774 | > case s of 775 | > Nil -> return () 776 | > Cons x l' -> do 777 | > putStrLn x 778 | > stdout l' 779 | -} 780 | data Step m a = Cons a (ListT m a) | Nil 781 | deriving (Foldable, Traversable) 782 | 783 | instance Monad m => Functor (Step m) where 784 | fmap _ Nil = Nil 785 | fmap k (Cons x l) = Cons (k x) (fmap k l) 786 | 787 | instance MFunctor Step where 788 | hoist _ Nil = Nil 789 | hoist f (Cons x xs) = Cons x (hoist f xs) 790 | 791 | -- | Similar to 'ZipList' in /base/: a newtype wrapper over 'ListT' that 792 | -- overrides its normal 'Applicative' instance (combine every combination) 793 | -- with one that "zips" outputs together one at a time. 794 | -- 795 | -- >>> let xs = do x <- select [1,2,3,4]; liftIO (print x) 796 | -- >>> let ys = do y <- select [5,6]; liftIO (print y) 797 | -- >>> runListT (xs *> ys) 798 | -- 1 799 | -- 5 800 | -- 6 801 | -- 2 802 | -- 5 803 | -- 6 804 | -- 3 805 | -- 5 806 | -- 6 807 | -- 4 808 | -- 5 809 | -- 6 810 | -- >>> runListT (getZipListT (ZipListT xs *> ZipListT ys)) 811 | -- 1 812 | -- 5 813 | -- 2 814 | -- 6 815 | -- 3 816 | -- 817 | -- Note that the final "3" is printed even though it isn't paired with 818 | -- anything. 819 | -- 820 | -- While this can be used to do zipping, it is usually more convenient to 821 | -- just use 'zip'. This is more useful if you are working with a function 822 | -- that expects "an Applicative instance", written to be polymorphic over 823 | -- all Applicatives. 824 | newtype ZipListT m a = ZipListT { getZipListT :: ListT m a } 825 | deriving (Functor, Alternative, Foldable, Traversable, Floating, Fractional, Num, Semigroup, Monoid) 826 | 827 | instance Monad m => Applicative (ZipListT m) where 828 | pure x = ZipListT go 829 | where 830 | #if MIN_VERSION_base(4,8,0) 831 | go = ListT (pure (Cons x go)) 832 | #else 833 | go = ListT (return (Cons x go)) 834 | #endif 835 | ZipListT fs <*> ZipListT xs = ZipListT (fmap (uncurry ($)) (zip fs xs)) 836 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-6.5 2 | -------------------------------------------------------------------------------- /test/DocTest.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Test.DocTest 4 | 5 | main :: IO () 6 | main = doctest 7 | [ "-XCPP" 8 | , "src/List/Transformer.hs" 9 | ] 10 | --------------------------------------------------------------------------------