Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.haskell > #508 > unrolled thread
| Started by | kfjwheeler@gmail.com |
|---|---|
| First post | 2018-12-11 05:19 -0800 |
| Last post | 2018-12-13 10:46 -0500 |
| Articles | 9 — 5 participants |
Back to article view | Back to comp.lang.haskell
Get mapM to be lazy for IO kfjwheeler@gmail.com - 2018-12-11 05:19 -0800
Re: Get mapM to be lazy for IO Mark Carroll <mtbc@bcs.org> - 2018-12-11 13:36 +0000
Re: Get mapM to be lazy for IO Kaylen Wheeler <kfjwheeler@gmail.com> - 2018-12-12 17:52 -0800
Re: Get mapM to be lazy for IO Paul Rubin <no.email@nospam.invalid> - 2018-12-12 19:29 -0800
Re: Get mapM to be lazy for IO Kaylen Wheeler <kfjwheeler@gmail.com> - 2018-12-12 18:21 -0800
Re: Get mapM to be lazy for IO Paul Rubin <no.email@nospam.invalid> - 2018-12-12 19:31 -0800
Re: Get mapM to be lazy for IO Barry Fishman <barry@ecubist.org> - 2018-12-13 09:52 -0500
Re: Get mapM to be lazy for IO Barry Fishman <barry@ecubist.org> - 2018-12-13 10:21 -0500
Re: Get mapM to be lazy for IO Barry Fishman <barry@ecubist.org> - 2018-12-13 10:46 -0500
| From | kfjwheeler@gmail.com |
|---|---|
| Date | 2018-12-11 05:19 -0800 |
| Subject | Get mapM to be lazy for IO |
| Message-ID | <24548724-4bdd-402d-8a76-1403b7e87169@googlegroups.com> |
I'm kind of a beginner when it comes to Haskell. Specifically with regards to IO.
I'm tying to write a very simple program that asks the user to input a list of words. It gets words until the user enters "done".
I'm getting close, but it's still asking for input after I enter "done". I can't seem to get mapM to be lazy in the way I want.
Here's what I have so far:
untilExit = do
putStrLn "Hello. Please enter words until 'done'."
words <- mapM (\n -> do
putStrLn $"Input number " ++ show n ++ ": "
l <- getLine
return l)
[1..3] >>= return . takeWhile (/="done")
putStrLn "Here are the words:"
mapM putStrLn $ takeWhile (/="done") words
return words
[toc] | [next] | [standalone]
| From | Mark Carroll <mtbc@bcs.org> |
|---|---|
| Date | 2018-12-11 13:36 +0000 |
| Message-ID | <87bm5s4249.fsf@ixod.org> |
| In reply to | #508 |
On 11 Dec 2018, kfjwheeler wrote: > I'm kind of a beginner when it comes to Haskell. Specifically with > regards to IO. You're making a promising start! (snip) > I'm getting close, but it's still asking for input after I enter > "done". I can't seem to get mapM to be lazy in the way I want. You're doing the three IO actions in sequence then selecting from their results. Broadly, with IO you don't get to skip stuff after the >>= just by not consuming the result. So, you need to move the check of the input value amid the sequence itself, only do further IO actions if necessary. You could try rolling that yourself (do another >>= only if necessary) or use the help of something like unfoldM, we'd be happy to help you with either. -- Mark
[toc] | [prev] | [next] | [standalone]
| From | Kaylen Wheeler <kfjwheeler@gmail.com> |
|---|---|
| Date | 2018-12-12 17:52 -0800 |
| Message-ID | <a9bc88e6-c0cc-4de0-85a0-32e76dc4eb17@googlegroups.com> |
| In reply to | #509 |
On Tuesday, 11 December 2018 08:36:08 UTC-5, Mark Carroll wrote:
> On 11 Dec 2018, kfjwheeler wrote:
>
> > I'm kind of a beginner when it comes to Haskell. Specifically with
> > regards to IO.
>
> You're making a promising start!
>
> (snip)
> > I'm getting close, but it's still asking for input after I enter
> > "done". I can't seem to get mapM to be lazy in the way I want.
>
> You're doing the three IO actions in sequence then selecting from their
> results. Broadly, with IO you don't get to skip stuff after the >>= just
> by not consuming the result. So, you need to move the check of the input
> value amid the sequence itself, only do further IO actions if necessary.
> You could try rolling that yourself (do another >>= only if necessary)
> or use the help of something like unfoldM, we'd be happy to help you
> with either.
>
> -- Mark
I thought I improved it a little, but it's still not lazy.
-- Some random I/O stuff
untilExit = do
putStrLn "Hello. Please enter words until 'done'."
words <- liftM (takeWhile (/= "done")) $
mapM (\n -> do
putStrLn $"Input number " ++ show n ++ ": "
l <- getLine
return l)
[1..3]
putStrLn "Here are the words:"
mapM putStrLn $ words
return words
[toc] | [prev] | [next] | [standalone]
| From | Paul Rubin <no.email@nospam.invalid> |
|---|---|
| Date | 2018-12-12 19:29 -0800 |
| Message-ID | <87wooep0i9.fsf@nightsong.com> |
| In reply to | #510 |
Kaylen Wheeler <kfjwheeler@gmail.com> writes:
> mapM (\n -> do
> putStrLn $"Input number " ++ show n ++ ": "
> l <- getLine
> return l)
> [1..3]
That mapM runs the three actions and binds them together. It's just
like mapM_ except it returns the result list. So you run all three
actions before looking for "done".
> mapM putStrLn $ words
In principle you want mapM_ there, though it won't matter.
This seems to work:
main = do
putStrLn "Hello. Please enter words until 'done'."
let go [] xs = return xs
go (n:ns) xs = do
putStrLn $"Input number " ++ show n ++ ": "
l <- getLine
if l == "done" then return xs
else go ns (l:xs)
words <- reverse `fmap` go [1..3] []
putStrLn "Here are the words:"
mapM_ putStrLn $ words
[toc] | [prev] | [next] | [standalone]
| From | Kaylen Wheeler <kfjwheeler@gmail.com> |
|---|---|
| Date | 2018-12-12 18:21 -0800 |
| Message-ID | <551a276a-536d-4ba1-af9f-9f471dcac4f9@googlegroups.com> |
| In reply to | #509 |
On Tuesday, 11 December 2018 08:36:08 UTC-5, Mark Carroll wrote:
> On 11 Dec 2018, kfjwheeler wrote:
>
> > I'm kind of a beginner when it comes to Haskell. Specifically with
> > regards to IO.
>
> You're making a promising start!
>
> (snip)
> > I'm getting close, but it's still asking for input after I enter
> > "done". I can't seem to get mapM to be lazy in the way I want.
>
> You're doing the three IO actions in sequence then selecting from their
> results. Broadly, with IO you don't get to skip stuff after the >>= just
> by not consuming the result. So, you need to move the check of the input
> value amid the sequence itself, only do further IO actions if necessary.
> You could try rolling that yourself (do another >>= only if necessary)
> or use the help of something like unfoldM, we'd be happy to help you
> with either.
>
> -- Mark
I think I was going down the wrong road here.
I think the following is a much better way of doing while-loop-like constructs:
easyUntilDone = do
putStrLn "Please enter words until 'done'."
words <- let loop words = do putStr $ "Input a word: "
l <- getLine
if l == "done"
then return $ reverse words
else loop (l:words)
in loop []
putStrLn $ "Here are the words: " ++ show words
[toc] | [prev] | [next] | [standalone]
| From | Paul Rubin <no.email@nospam.invalid> |
|---|---|
| Date | 2018-12-12 19:31 -0800 |
| Message-ID | <87sgz2p0fv.fsf@nightsong.com> |
| In reply to | #511 |
Kaylen Wheeler <kfjwheeler@gmail.com> writes: > I think the following is a much better way of doing while-loop-like > constructs: Yes, that's more direct. You can also look at Control.Monad.Loops for some useful combinators.
[toc] | [prev] | [next] | [standalone]
| From | Barry Fishman <barry@ecubist.org> |
|---|---|
| Date | 2018-12-13 09:52 -0500 |
| Message-ID | <7nmup95via.fsf@ecube.ecubist.org> |
| In reply to | #508 |
On 2018-12-11 05:19:18 -08, kfjwheeler@gmail.com wrote:
> I'm kind of a beginner when it comes to Haskell. Specifically with regards to IO.
>
> I'm tying to write a very simple program that asks the user to input a list of words. It gets words until the user enters "done".
>
> I'm getting close, but it's still asking for input after I enter "done". I can't seem to get mapM to be lazy in the way I want.
IO can be lazy, but the order of actions needs to preserved, so that
each putStrLn prompt precedes it's getLine.
The following is how I might write it. I used the 'hFlush' call so I
could put out the prompt without forcing a newline, and then read the
word on the same line. You can leave it out.
--8<---------------cut here---------------start------------->8---
#! /usr/bin/env runghc
import System.IO (hFlush, stdout)
main :: IO ()
main = do
putStrLn "Hello. Please enter words until 'done'."
xs <- getWordList 1
mapM_ putStrLn xs
where getWordList :: Int -> IO [String]
getWordList n = do
putStr $ "Input number " ++ show n ++ ": "
hFlush stdout
x <- getLine
if x == "done"
then return []
else do
xs <- getWordList (n + 1)
return (x : xs)
--8<---------------cut here---------------end--------------->8---
I would probably shorted the last 'else' to:
else (x:) `fmap` getWordList (n + 1)
--
Barry Fishman
[toc] | [prev] | [next] | [standalone]
| From | Barry Fishman <barry@ecubist.org> |
|---|---|
| Date | 2018-12-13 10:21 -0500 |
| Message-ID | <7nimzx5u6q.fsf@ecube.ecubist.org> |
| In reply to | #514 |
On 2018-12-13 09:52:45 -05, Barry Fishman wrote: > On 2018-12-11 05:19:18 -08, kfjwheeler@gmail.com wrote: >> I'm kind of a beginner when it comes to Haskell. Specifically with >> regards to IO. >> >> I'm tying to write a very simple program that asks the user to input >> a list of words. It gets words until the user enters "done". >> >> I'm getting close, but it's still asking for input after I enter >> "done". I can't seem to get mapM to be lazy in the way I want. > > IO can be lazy, but the order of actions needs to preserved, so that > each putStrLn prompt precedes it's getLine. Oops, I was wrong. If I replace the: xs <- getWordList 1 with: xs <- take 3 `fmap` getWordList 1 the values continue to be read. So the IO can't be read lazily as far as I can tell. -- Barry Fishman
[toc] | [prev] | [next] | [standalone]
| From | Barry Fishman <barry@ecubist.org> |
|---|---|
| Date | 2018-12-13 10:46 -0500 |
| Message-ID | <7nefal5t0g.fsf@ecube.ecubist.org> |
| In reply to | #515 |
On 2018-12-13 10:21:17 -05, Barry Fishman wrote:
> Oops, I was wrong. If I replace the:
>
> xs <- getWordList 1
>
> with:
>
> xs <- take 3 `fmap` getWordList 1
>
> the values continue to be read. So the IO can't be read lazily as far
> as I can tell.
All I can seem to do is produce a infinite lazy list of IO actions,
prompting for and reading the words:
--8<---------------cut here---------------start------------->8---
#! /usr/bin/env runghc
import System.IO (hFlush, stdout)
main :: IO ()
main = do
putStrLn "Hello. Please enter each word."
xs <- sequence . take 3 $ map getWordIO [1..]
mapM_ putStrLn xs
where getWordIO :: Int -> IO String
getWordIO n = do
putStr $ "Input number " ++ show n ++ ": "
hFlush stdout
getLine
--8<---------------cut here---------------end--------------->8---
So the list that I 'take 3' from is a '[IO String]' and I sequence though
it to produce an 'IO [String]'.
--
Barry Fishman
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.haskell
csiph-web