transformers-free (empty) → 1.0.0
raw patch · 4 files changed
+306/−0 lines, 4 filesdep +basedep +transformerssetup-changed
Dependencies added: base, transformers
Files
- Control/Monad/Trans/Free.hs +255/−0
- LICENSE +24/−0
- Setup.hs +2/−0
- transformers-free.cabal +25/−0
+ Control/Monad/Trans/Free.hs view
@@ -0,0 +1,255 @@+{-|+ Free monads build syntax trees. See the example sections for details.++ A free monad over a functor resembles a list of that functor:++ * 'return' behaves like @[]@ by not using the functor at all++ * 'wrap' behaves like @(:)@ by prepending another layer of the functor++ * 'liftF' behaves like @singleton@ by creating a list from a single layer of+ the functor.+-}++module Control.Monad.Trans.Free (+ -- * Usage+ -- $usage++ -- * Free monad+ Free,+ FreeF(..),+ runFree,++ -- * Free monad transformer+ FreeT(..),++ -- * Free monad operations+ wrap,+ liftF++ -- * Free monad example+ -- $freeexample++ -- * Free monad transformer example+ -- $freetexample+ ) where++import Control.Applicative+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++{- $usage+ You can assemble values of type @Free f a@ or @FreeT f m a@ by hand using+ the smart constructors 'return' (from @Control.Monad@) and 'wrap':++> return :: r -> FreeT f m r+> wrap :: f (FreeT f m r) -> FreeT f m r++ Use 'runFree' to deconstruct values of type @Free f r@:++> case runFree x of+> Pure a -> ...+> Free w -> ...++ Use 'runFreeT' to deconstruct values of type @FreeT f m r@ and bind the+ result in the base monad @m@. You can then pattern match against the bound+ value:++> do x <- runFreeT f+> case x of+> Pure a -> ...+> Free w -> ...+-}++-- | The signature for 'Free'+data FreeF f a x = Pure a | Free (f x)++{-|+ A free monad transformer alternates nesting the base monad @m@ and the base+ functor @f@, terminating with a value of type @a@.++ * @f@ - The functor that generates the free monad transformer++ * @m@ - The base monad++ * @a@ - The return value+-}+newtype FreeT f m a = FreeT { runFreeT :: m (FreeF f a (FreeT f m a)) }++instance (Functor f, Monad m) => Functor (FreeT f m) where+ fmap = liftM++instance (Functor f, Monad m) => Applicative (FreeT f m) where+ pure = return+ (<*>) = ap++instance (Functor f, Monad m) => Monad (FreeT f m) where+ return = FreeT . return . Pure+ m >>= f = FreeT $ do+ x <- runFreeT m+ runFreeT $ case x of+ Pure a -> f a+ Free w -> wrap $ fmap (>>= f) w++instance MonadTrans (FreeT f) where+ lift = FreeT . liftM Pure++instance (Functor f, MonadIO m) => MonadIO (FreeT f m) where+ liftIO = lift . liftIO ++-- | Prepend one layer of the functor to the free monad+wrap :: (Monad m) => f (FreeT f m a) -> FreeT f m a+wrap = FreeT . return . Free++-- | Convert one layer of a functor into an operation in the free monad+liftF :: (Functor f, Monad m) => f a -> FreeT f m a+liftF x = wrap $ fmap return x++{-|+ @Free f a@ is a list of nested @f@s terminating with a return value of type+ @a@.++ * @f@ - The functor that generates the free monad++ * @a@ - The return value+-}+type Free f = FreeT f Identity++-- | Observation function that exposes the next step+runFree :: Free f r -> FreeF f r (Free f r)+runFree = runIdentity . runFreeT++{- $freeexample+ To create a syntax tree, first define the signature for a single step in the+ syntax tree:++> data TeletypeF next = PutString String next | GetString (String -> next)++ ... then make the signature a 'Functor', where 'fmap' applies the given+ function to the @next@ step:++> instance Functor TeletypeF where+> fmap f (PutString str x) = PutString str (f x)+> fmap f (GetString k) = GetString (f . k)++ The 'Free' type constructor generates the corresponding syntax tree from+ this signature:++> type Teletype a = Free TeletypeF a++ 'liftF' creates primitive operations for building the syntax tree:++> putString :: String -> Teletype ()+> putString str = liftF $ PutString str ()+>+> getString :: Teletype String+> getString = liftF $ GetString id++ The syntax tree is automatically a monad, so you can assemble these+ operations into larger syntax trees using @do@ notation:++> echo :: Teletype a+> echo = forever $ do+> str <- getString+> putString str++ ... which is equivalent to the following hand-written syntax tree:++> echo' :: Teletype r+> echo' = wrap $ GetString $ \str -> wrap $ PutString str echo'++ You then interpret the syntax tree using 'runFree' to inspect the tree one+ step at a time.++> runIO :: Teletype a -> IO a+> runIO t = case runFree t of+> Pure r -> return r+> Free (PutString str t') -> do+> putStrLn str+> runIO t'+> Free (GetString k ) -> do+> str <- getLine+> runIO (k str)++>>> runIO echo+A<Enter>+A+Test<Enter>+Test+...++ You can write pure interpreters, too:++> runPure :: Teletype a -> [String] -> [String]+> runPure t strs = case runFree t of+> Pure r -> []+> Free (PutString str t') -> str:runPure t' strs+> Free (GetString k ) -> case strs of+> [] -> []+> str:strs' -> runPure (k str) strs'++>>> runPure echo ["A", "Test"]+["A","Test"]+-}++{- $freetexample+ The Free monad transformer 'FreeT' lets us invoke the base monad to build+ the syntax tree. For example, you can use 'IO' to prompt the user to select+ each step of the syntax tree using the following monad:++> FreeT TeletypeF IO r++ Our original primitives actually had the following more polymorphic types,+ so you can reuse them:++> putString :: (Monad m) => String -> FreeT TeletypeF m ()+> putString str = liftF $ PutString str ()+>+> getString :: (Monad m) => FreeT TeletypeF m String+> getString = liftF $ GetString id++ Now the user can build the syntax tree from the command line:++> prompt :: FreeT TeletypeF IO ()+> prompt = do+> lift $ putStrLn "Supply the next step:+> cmd <- lift getLine+> case cmd of+> "forward" -> do+> str <- getString+> putString str+> prompt+> "greet" -> do+> putString "Hello, world!"+> prompt+> _ -> return ()++ You can then run the syntax tree as the user builds it:++> -- The 'FreeT' version of 'runIO'+> runTIO :: FreeT TeletypeF IO r -> IO r+> runTIO t = do+> x <- runFreeT t+> case x of+> Pure r -> return r+> Free (PutString str t') -> do+> putStrLn str+> runTIO t'+> Free (GetString k) -> do+> str <- getLine+> runTIO (k str)++>>> runTIO prompt+Supply the next step:+greet<Enter>+Hello, world!+Supply the next step:+forward<Enter>+test<Enter>+test+Supply the next step:+quit<Enter>++-}
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2012, Gabriel 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 Gabriel 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ transformers-free.cabal view
@@ -0,0 +1,25 @@+Name: transformers-free+Version: 1.0.0+Cabal-Version: >=1.14.0+Build-Type: Simple+License: BSD3+License-File: LICENSE+Copyright: 2012 Gabriel Gonzalez+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Stability: Experimental+Bug-Reports:+ https://github.com/Gabriel439/Haskell-Transformers-Free-Library/issues+Synopsis: Free monad transformers+Description: This library generalizes free monads to free monad transformers.+Category: Control+Tested-With: GHC ==7.0.3+Source-Repository head+ Type: git+ Location: https://github.com/Gabriel439/Haskell-Transformers-Free-Library++Library+ Build-Depends: base >= 2 && < 6, transformers >= 0.2+ Exposed-Modules: Control.Monad.Trans.Free+ GHC-Options: -O2+ Default-Language: Haskell98