[Haskell-beginners] lazy mapM

Ovidiu D ovidiudeac at gmail.com
Tue Apr 2 23:05:21 CEST 2013


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 <toad3k at gmail.com> 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 <toad3k at gmail.com> 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 <ovidiudeac at gmail.com> 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 <toad3k at gmail.com> 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 <ovidiudeac at gmail.com> 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
>>>>> Beginners at haskell.org
>>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>>
>>>>>
>>>>
>>>> _______________________________________________
>>>> Beginners mailing list
>>>> Beginners at haskell.org
>>>> http://www.haskell.org/mailman/listinfo/beginners
>>>>
>>>>
>>>
>>> _______________________________________________
>>> Beginners mailing list
>>> Beginners at haskell.org
>>> http://www.haskell.org/mailman/listinfo/beginners
>>>
>>>
>>
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/beginners/attachments/20130403/03a4d6cc/attachment-0001.htm>


More information about the Beginners mailing list