Send Beginners mailing list submissions to
        [email protected]

To subscribe or unsubscribe via the World Wide Web, visit
        http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
        [email protected]

You can reach the person managing the list at
        [email protected]

When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."


Today's Topics:

   1. Re:  lazy mapM (Ovidiu D)
   2. Re:  lazy mapM (David McBride)


----------------------------------------------------------------------

Message: 1
Date: Wed, 3 Apr 2013 03:39:26 +0300
From: Ovidiu D <[email protected]>
Subject: Re: [Haskell-beginners] lazy mapM
To: The Haskell-Beginners Mailing List - Discussion of primarily
        beginner-level topics related to Haskell <[email protected]>
Message-ID:
        <CAKVsE7uxC99SfA2Ku=k9cqfwkyph4nr8ajx+rhqvwdhmjfx...@mail.gmail.com>
Content-Type: text/plain; charset="iso-8859-1"

Trying to understand the conduits and looking for "the clean way to do it"
I got to the code below (based on David's conduit example).

I'm quite happy with the result. My problem is that I had to write
functions like conduitTakeWhile and conduitSource (see them at the bottom
of the code). Are there any functions like this in the conduit library
which I'm missing somehow? Is there a way to emulate this behaviour with
the existing functions from conduit package?



import Data.Conduit
import Data.Conduit.List as L
import System.IO
import Control.Monad.Trans

main = do
  hSetBuffering stdout NoBuffering
  runProcessPipe

runProcessPipe =
  conduitSource readCommand
  $= conduitTakeWhile ( "exit" /=)
  =$= L.map processCommand
  $$ L.mapM_ $ putStr.unlines

readCommand :: IO (Maybe String)
readCommand = do
    putStr ">> "
    isEof <- hIsEOF stdin
    if isEof
        then return Nothing
        else getLine >>= return . Just

processCommand cmd = ["reversed string:",reverse cmd]

-- Utilities

conduitSource :: Monad m => (m (Maybe a)) -> Source m a
conduitSource f = do
    v <- lift f
    case v of
        Nothing -> return ()
        Just x -> yield x >> conduitSource f

conduitTakeWhile :: Monad m => ( a -> Bool ) -> Conduit a m a
conduitTakeWhile p = do
    cmd <- await
    case cmd of
        Nothing -> return ()
        Just v -> do
            if p v
                then yield v >> conduitTakeWhile p
                else return ()



On Wed, Apr 3, 2013 at 12:05 AM, Ovidiu D <[email protected]> wrote:

> I managed to compile and it works but I don't full understand all the type
> details. I'll have to dig into Pipes and Conduits.
>
> Thanks a lot for the code!
>
>
>
> On Mon, Apr 1, 2013 at 5:51 AM, David McBride <[email protected]> wrote:
>
>> I'm sorry I jacked up the code editing my email inline, the pipes section
>> below main should look like this:
>>
>> commandProducer :: Producer String IO ()
>>
>> commandProducer = do
>>   x <- lift getLine
>>   if x == "exit"
>>     then return ()
>>     else P.yield x >> commandProducer
>>
>> displayConsumer :: PrintfArg a => Consumer a IO ()
>>
>> displayConsumer = forever $ P.await >>= lift . printf "Command not
>> implemented (pipes): '%s'\n"
>>
>>
>>
>> On Sun, Mar 31, 2013 at 10:49 PM, David McBride <[email protected]> wrote:
>>
>>> Doing it the way you are trying to do it breaks the IO abstraction.  In
>>> order to do it you'd have to use unsafe functions.  Unsafe functions are
>>> bad.  I'm not going to explain why but they tend to bite you as your
>>> program gets more complex and weirdness starts to occur, like threads
>>> ceasing operation while awaiting input is something that bit me when I went
>>> down that route.  So let me explain how I would do it using both pipes and
>>> conduits as examples:
>>>
>>> import Data.Conduit as C hiding ((>+>), runPipe)
>>> import System.IO
>>> import Control.Monad.Trans
>>> import Text.Printf.Mauke
>>>
>>> import Control.Pipe as P
>>> import Control.Monad (forever)
>>>
>>> -- Source runs in the IO monad and produces Strings
>>> commandSource :: Source IO String
>>> commandSource = do
>>>   command <- liftIO getLine
>>>   if command == "exit"
>>>     then return ()
>>>     else do
>>>       C.yield command
>>>       commandSource -- loop to fetching new values to send down the pipe
>>>
>>> -- Sink runs in the IO monad and takes any printfable argument and
>>> returns () when pipe completes.
>>> displaySink :: PrintfArg a => Sink a IO ()
>>> displaySink = do
>>>   m <- C.await
>>>   case m of
>>>     Nothing -> return ()  -- if nothing comes in, just exit
>>>     Just x -> do
>>>       liftIO $ printf "Command not implemented (conduit): '%s'\n" x
>>>       displaySink
>>>
>>> main = do
>>>   hSetBuffering stdout NoBuffering
>>>   commandSource $$ displaySink
>>>   runPipe $ commandProducer >+> displayConsumer
>>>
>>>
>>> commandProducer :: PrintfArg a => Producer a String IO ()
>>> commandProducer = do
>>>   x <- lift getLine
>>>   if x == "exit"
>>>     then return ()
>>>     else P.yield x >> commandProducer
>>>
>>> displayConsumer :: Consumer String IO ()
>>> displayConsumer = forever $ P.await >>= lift . printf "Command not
>>> implemented (pipes): '%s'\n"
>>>
>>> There are some utility function to shorten some of these definitions a
>>> bit in conduit.  These two examples are equivalent.  But basically you are
>>> creating a pipeline, the first of which gets commands until it gets an exit
>>> and then sends them down the pipeline (as a string).  The second piece of
>>> the pipe accepts anything that is printfable and prints it.  It will stop
>>> when the upstream stops sending it strings to print.  The point here is
>>> that you have little functions that you can compose together with other
>>> functions and create something bigger where none of the pieces interfere
>>> with each other or break the IO abstraction.
>>>
>>> As to which of these libraries you should try?  Conduits is a bit more
>>> straight forward and has a lot more documentation and supporting
>>> libraries.  Pipes is a lot more flexible in that you could send things both
>>> directions along the pipe in the future when you become proficient with the
>>> library.
>>>
>>>
>>>
>>>
>>> On Sun, Mar 31, 2013 at 9:38 PM, Ovidiu D <[email protected]> wrote:
>>>
>>>> I'm not sure I understand what you mean by "I know you have the best
>>>> intentions in writing this, but there are pitfalls.". Anyway, here's the
>>>> code which doesn't work apparently because mapM is waiting for the whole
>>>> list before it goes further.
>>>>
>>>> prompt = ">> "
>>>>
>>>> commands :: [IO String]
>>>> commands = readCommand : commands
>>>>     where readCommand = putStr prompt >> getLine
>>>>
>>>> display :: Show a => [ a ] -> IO ()
>>>> display = mapM_ $ putStr . show
>>>>
>>>> executeCommand :: String -> String
>>>> executeCommand = printf "Command not implemented: '%s'"
>>>>
>>>> processCommands :: [IO String] -> IO [ String ]
>>>> processCommands = mapM processOneCommand
>>>>     where processOneCommand cmd = cmd >>= (return . executeCommand )
>>>>
>>>> main =
>>>>     hSetBuffering stdout NoBuffering
>>>>     >> processCommands commands
>>>>     >>= display
>>>>
>>>> This is just for learning purposes and I'm looking for the "haskell way
>>>> to do it". My intention is to write the function processCommands such that
>>>> it takes the decision to either fetch the next command from the command
>>>> list (i.e. console) or to exit the application.
>>>>
>>>> Regarding your comment "Just know that at some point you should learn
>>>> to use conduits or pipes for a much better approach to modeling things like
>>>> this.". Can you point me to some documentation?
>>>>
>>>> Thanks!
>>>>
>>>>
>>>> On Mon, Apr 1, 2013 at 3:53 AM, David McBride <[email protected]> wrote:
>>>>
>>>>> I know you have the best intentions in writing this, but there are
>>>>> pitfalls.  Unexpected things happen when you interleave IO in this manner,
>>>>> but nonetheless, here's how you would do it.
>>>>>
>>>>> myGetLine = do
>>>>>   x <- getLine
>>>>>   if (x == "exit")
>>>>>       then return []
>>>>>       else do
>>>>>         xs <- unsafeInterleaveIO myGetLine
>>>>>         return (x:xs)
>>>>>
>>>>> main = do
>>>>>   x <- myGetLine
>>>>>   print x
>>>>>
>>>>> Just know that at some point you should learn to use conduits or pipes
>>>>> for a much better approach to modeling things like this.
>>>>>
>>>>>
>>>>>
>>>>> On Sun, Mar 31, 2013 at 7:26 PM, Ovidiu D <[email protected]>wrote:
>>>>>
>>>>>>  Hi again,
>>>>>>
>>>>>> Given the following code:
>>>>>>
>>>>>> g :: IO String -> IO String
>>>>>>
>>>>>> f :: [IO String] -> IO [ String ]
>>>>>> f = mapM g
>>>>>>
>>>>>> The implementation of f is wrong because I would like to:
>>>>>> 1. Make f behave lazy
>>>>>> Its input list is made of lines read from stdin and I want it to
>>>>>> process lines one by one as they are entered by the user.
>>>>>>
>>>>>> 2. Implement  f such that it stops consuming items from the input
>>>>>> list when the input item meets some condition. For example:
>>>>>> isExit item = ("exit" == item)
>>>>>>
>>>>>> I tried to implement my own custom iteration by recursion but I got
>>>>>> stuck in the combination of IO and list monads.
>>>>>>
>>>>>> Any help is appreciated.
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> Beginners mailing list
>>>>>> [email protected]
>>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>>
>>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Beginners mailing list
>>>>> [email protected]
>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>
>>>>>
>>>>
>>>> _______________________________________________
>>>> Beginners mailing list
>>>> [email protected]
>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>
>>>>
>>>
>>
>> _______________________________________________
>> Beginners mailing list
>> [email protected]
>> http://www.haskell.org/mailman/listinfo/beginners
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
<http://www.haskell.org/pipermail/beginners/attachments/20130403/3340a379/attachment-0001.htm>

------------------------------

Message: 2
Date: Tue, 2 Apr 2013 21:50:35 -0400
From: David McBride <[email protected]>
Subject: Re: [Haskell-beginners] lazy mapM
To: The Haskell-Beginners Mailing List - Discussion of primarily
        beginner-level topics related to Haskell <[email protected]>
Message-ID:
        <CAN+Tr40CZpmh7JSq_6-bqaip3u=1z+y9bqfc+9ij3ror7v8...@mail.gmail.com>
Content-Type: text/plain; charset="iso-8859-1"

Actually, I'm not sure there is.  I can make a few minor improvements, but
that's about it.  When chaining conduits together, they are kind of weird,
but a general way that I string them together would be like this:

runProcessPipe =
  conduitSource readCommand
    $= conduitTakeWhile ( "exit" /=)
    $= CL.map processCommand
    $$ CL.mapM_ (putStr . unlines)

Where each source combines with a conduit to make a new source, which
combines with next conduit, etc and then the last sink uses a $$ to finish
it off.  But other than that I think you have a handle on it.


On Tue, Apr 2, 2013 at 8:39 PM, Ovidiu D <[email protected]> wrote:

> Trying to understand the conduits and looking for "the clean way to do it"
> I got to the code below (based on David's conduit example).
>
> I'm quite happy with the result. My problem is that I had to write
> functions like conduitTakeWhile and conduitSource (see them at the bottom
> of the code). Are there any functions like this in the conduit library
> which I'm missing somehow? Is there a way to emulate this behaviour with
> the existing functions from conduit package?
>
>
>
> import Data.Conduit
> import Data.Conduit.List as L
> import System.IO
> import Control.Monad.Trans
>
>
> main = do
>   hSetBuffering stdout NoBuffering
>   runProcessPipe
>
> runProcessPipe =
>   conduitSource readCommand
>   $= conduitTakeWhile ( "exit" /=)
>   =$= L.map processCommand
>   $$ L.mapM_ $ putStr.unlines
>
> readCommand :: IO (Maybe String)
> readCommand = do
>     putStr ">> "
>     isEof <- hIsEOF stdin
>     if isEof
>         then return Nothing
>         else getLine >>= return . Just
>
> processCommand cmd = ["reversed string:",reverse cmd]
>
> -- Utilities
>
> conduitSource :: Monad m => (m (Maybe a)) -> Source m a
> conduitSource f = do
>     v <- lift f
>
>     case v of
>         Nothing -> return ()
>         Just x -> yield x >> conduitSource f
>
> conduitTakeWhile :: Monad m => ( a -> Bool ) -> Conduit a m a
> conduitTakeWhile p = do
>     cmd <- await
>     case cmd of
>         Nothing -> return ()
>         Just v -> do
>             if p v
>                 then yield v >> conduitTakeWhile p
>                 else return ()
>
>
>
> On Wed, Apr 3, 2013 at 12:05 AM, Ovidiu D <[email protected]> wrote:
>
>> I managed to compile and it works but I don't full understand all the
>> type details. I'll have to dig into Pipes and Conduits.
>>
>> Thanks a lot for the code!
>>
>>
>>
>> On Mon, Apr 1, 2013 at 5:51 AM, David McBride <[email protected]> wrote:
>>
>>> I'm sorry I jacked up the code editing my email inline, the pipes
>>> section below main should look like this:
>>>
>>> commandProducer :: Producer String IO ()
>>>
>>> commandProducer = do
>>>   x <- lift getLine
>>>   if x == "exit"
>>>     then return ()
>>>     else P.yield x >> commandProducer
>>>
>>> displayConsumer :: PrintfArg a => Consumer a IO ()
>>>
>>> displayConsumer = forever $ P.await >>= lift . printf "Command not
>>> implemented (pipes): '%s'\n"
>>>
>>>
>>>
>>> On Sun, Mar 31, 2013 at 10:49 PM, David McBride <[email protected]>wrote:
>>>
>>>> Doing it the way you are trying to do it breaks the IO abstraction.  In
>>>> order to do it you'd have to use unsafe functions.  Unsafe functions are
>>>> bad.  I'm not going to explain why but they tend to bite you as your
>>>> program gets more complex and weirdness starts to occur, like threads
>>>> ceasing operation while awaiting input is something that bit me when I went
>>>> down that route.  So let me explain how I would do it using both pipes and
>>>> conduits as examples:
>>>>
>>>> import Data.Conduit as C hiding ((>+>), runPipe)
>>>> import System.IO
>>>> import Control.Monad.Trans
>>>> import Text.Printf.Mauke
>>>>
>>>> import Control.Pipe as P
>>>> import Control.Monad (forever)
>>>>
>>>> -- Source runs in the IO monad and produces Strings
>>>> commandSource :: Source IO String
>>>> commandSource = do
>>>>   command <- liftIO getLine
>>>>   if command == "exit"
>>>>     then return ()
>>>>     else do
>>>>       C.yield command
>>>>       commandSource -- loop to fetching new values to send down the pipe
>>>>
>>>> -- Sink runs in the IO monad and takes any printfable argument and
>>>> returns () when pipe completes.
>>>> displaySink :: PrintfArg a => Sink a IO ()
>>>> displaySink = do
>>>>   m <- C.await
>>>>   case m of
>>>>     Nothing -> return ()  -- if nothing comes in, just exit
>>>>     Just x -> do
>>>>       liftIO $ printf "Command not implemented (conduit): '%s'\n" x
>>>>       displaySink
>>>>
>>>> main = do
>>>>   hSetBuffering stdout NoBuffering
>>>>   commandSource $$ displaySink
>>>>   runPipe $ commandProducer >+> displayConsumer
>>>>
>>>>
>>>> commandProducer :: PrintfArg a => Producer a String IO ()
>>>> commandProducer = do
>>>>   x <- lift getLine
>>>>   if x == "exit"
>>>>     then return ()
>>>>     else P.yield x >> commandProducer
>>>>
>>>> displayConsumer :: Consumer String IO ()
>>>> displayConsumer = forever $ P.await >>= lift . printf "Command not
>>>> implemented (pipes): '%s'\n"
>>>>
>>>> There are some utility function to shorten some of these definitions a
>>>> bit in conduit.  These two examples are equivalent.  But basically you are
>>>> creating a pipeline, the first of which gets commands until it gets an exit
>>>> and then sends them down the pipeline (as a string).  The second piece of
>>>> the pipe accepts anything that is printfable and prints it.  It will stop
>>>> when the upstream stops sending it strings to print.  The point here is
>>>> that you have little functions that you can compose together with other
>>>> functions and create something bigger where none of the pieces interfere
>>>> with each other or break the IO abstraction.
>>>>
>>>> As to which of these libraries you should try?  Conduits is a bit more
>>>> straight forward and has a lot more documentation and supporting
>>>> libraries.  Pipes is a lot more flexible in that you could send things both
>>>> directions along the pipe in the future when you become proficient with the
>>>> library.
>>>>
>>>>
>>>>
>>>>
>>>> On Sun, Mar 31, 2013 at 9:38 PM, Ovidiu D <[email protected]> wrote:
>>>>
>>>>> I'm not sure I understand what you mean by "I know you have the best
>>>>> intentions in writing this, but there are pitfalls.". Anyway, here's the
>>>>> code which doesn't work apparently because mapM is waiting for the whole
>>>>> list before it goes further.
>>>>>
>>>>> prompt = ">> "
>>>>>
>>>>> commands :: [IO String]
>>>>> commands = readCommand : commands
>>>>>     where readCommand = putStr prompt >> getLine
>>>>>
>>>>> display :: Show a => [ a ] -> IO ()
>>>>> display = mapM_ $ putStr . show
>>>>>
>>>>> executeCommand :: String -> String
>>>>> executeCommand = printf "Command not implemented: '%s'"
>>>>>
>>>>> processCommands :: [IO String] -> IO [ String ]
>>>>> processCommands = mapM processOneCommand
>>>>>     where processOneCommand cmd = cmd >>= (return . executeCommand )
>>>>>
>>>>> main =
>>>>>     hSetBuffering stdout NoBuffering
>>>>>     >> processCommands commands
>>>>>     >>= display
>>>>>
>>>>> This is just for learning purposes and I'm looking for the "haskell
>>>>> way to do it". My intention is to write the function processCommands such
>>>>> that it takes the decision to either fetch the next command from the
>>>>> command list (i.e. console) or to exit the application.
>>>>>
>>>>> Regarding your comment "Just know that at some point you should learn
>>>>> to use conduits or pipes for a much better approach to modeling things 
>>>>> like
>>>>> this.". Can you point me to some documentation?
>>>>>
>>>>> Thanks!
>>>>>
>>>>>
>>>>> On Mon, Apr 1, 2013 at 3:53 AM, David McBride <[email protected]>wrote:
>>>>>
>>>>>> I know you have the best intentions in writing this, but there are
>>>>>> pitfalls.  Unexpected things happen when you interleave IO in this 
>>>>>> manner,
>>>>>> but nonetheless, here's how you would do it.
>>>>>>
>>>>>> myGetLine = do
>>>>>>   x <- getLine
>>>>>>   if (x == "exit")
>>>>>>       then return []
>>>>>>       else do
>>>>>>         xs <- unsafeInterleaveIO myGetLine
>>>>>>         return (x:xs)
>>>>>>
>>>>>> main = do
>>>>>>   x <- myGetLine
>>>>>>   print x
>>>>>>
>>>>>> Just know that at some point you should learn to use conduits or
>>>>>> pipes for a much better approach to modeling things like this.
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Sun, Mar 31, 2013 at 7:26 PM, Ovidiu D <[email protected]>wrote:
>>>>>>
>>>>>>>  Hi again,
>>>>>>>
>>>>>>> Given the following code:
>>>>>>>
>>>>>>> g :: IO String -> IO String
>>>>>>>
>>>>>>> f :: [IO String] -> IO [ String ]
>>>>>>> f = mapM g
>>>>>>>
>>>>>>> The implementation of f is wrong because I would like to:
>>>>>>> 1. Make f behave lazy
>>>>>>> Its input list is made of lines read from stdin and I want it to
>>>>>>> process lines one by one as they are entered by the user.
>>>>>>>
>>>>>>> 2. Implement  f such that it stops consuming items from the input
>>>>>>> list when the input item meets some condition. For example:
>>>>>>> isExit item = ("exit" == item)
>>>>>>>
>>>>>>> I tried to implement my own custom iteration by recursion but I got
>>>>>>> stuck in the combination of IO and list monads.
>>>>>>>
>>>>>>> Any help is appreciated.
>>>>>>>
>>>>>>> Thanks!
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> Beginners mailing list
>>>>>>> [email protected]
>>>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> Beginners mailing list
>>>>>> [email protected]
>>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>>
>>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Beginners mailing list
>>>>> [email protected]
>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>
>>>>>
>>>>
>>>
>>> _______________________________________________
>>> Beginners mailing list
>>> [email protected]
>>> http://www.haskell.org/mailman/listinfo/beginners
>>>
>>>
>>
>
> _______________________________________________
> Beginners mailing list
> [email protected]
> http://www.haskell.org/mailman/listinfo/beginners
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
<http://www.haskell.org/pipermail/beginners/attachments/20130402/154c988e/attachment.htm>

------------------------------

_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners


End of Beginners Digest, Vol 58, Issue 7
****************************************

Reply via email to