6

I'm having a little Haskell Situation over here. I'm trying to write two functions with monads. First one is supposed to iterate through a function as long as the condition is true for the input / output of the function. Second one is supposed to use the first one to take a number as input and write it as output until you enter a space.

I'm stuck with this, any help?

module Test where

while :: (a -> Bool) -> (a -> IO a) -> a -> IO a
while praed funktion x = do
                         f <- praed (funktion x)
                         if f == True then do
                                             y <- funktion x
                                             while praed funktion y
                         else return x



power2 :: IO ()
power2 = do putStr (Please enter a number.")
            i <- getChar
            while praed funktion
            where praed x = if x /= ' ' then False else True
                  funktion = i
2
  • 3
    So which one of these is your question about? Where are you stuck, what doesn't work? Commented Jul 18, 2013 at 9:45
  • Both of these functions don't even compile yet, and I'm pretty sure they wouldn't do what I want them to do anyways. I just don't know how to write it in a working version. I'm basically trying to get a while loop working with a condition. So for example, while odd (square 3) should test if square 3 is odd, then use the result of square 3 = 9 and do while odd (square 9) and so on and on. Technically it shouldn't need an x, it should just work with while condition function Commented Jul 18, 2013 at 9:51

2 Answers 2

12
import Control.Monad

while :: (a -> Bool) -> (a -> IO a) -> a -> IO a
while praed funktion x
    | praed x   = do
        y <- funktion x
        while praed funktion y
    | otherwise = return x


power2 :: IO ()
power2 = do
    putStr "Please enter a number."
    i <- getChar
    let praed x = x /= ' '
    let f x = do
        putChar x
        getChar
    while praed f '?'
    return ()

Some notes:

  • Using if x then True else False is redundant, it's equivalent to just x.
  • Similarly if x == True ... is redundant and equivalent to if x ....
  • You need to distinguish between IO actions and their results. For example, if yo do

    do
        i <- getChar
        ...
    

    then in ... i represents the result of the action, a character, so i :: Char. But getChar :: IO Char is the action itself. You can view it as a recipe that returns Char when performed. You can pass the recipe around to functions etc., and it is only performed when executed somewhere.

  • Your while called funktion twice, which probably isn't what you intend - it would read a character twice, check the first one and return the second one. Remember, your funktion is an action, so each time you "invoke" the action (for example by using <- funktion ... in the do notation), the action is run again. So it should rather be something like

    do
        y <- funktion x
        f <- praed y
        -- ...
    

    (My code is somewhat different, it checks the argument that is passed to it.)

Sign up to request clarification or add additional context in comments.

3 Comments

Awesome, thank you. I understand how you did power2, makes a lot of sense. I'm still not sure what exactly is happening in while, tho. At which point exactly does praed check if the condition is true?
@Chris It checks if the condition holds for its argument of type a before doing anything else, just like while statements in functional languages do. This is done using guards, which is often preferable over if/then/else. The line | praed x = do ... says if the condition praed x is true, proceed with .... The next line | otherwise = ... covers all the rest cases (because otherwise is just a synonym for True, so otherwise is a condition that is always satisfied).
if x then True else False isn't quite equivalent to x, it's equivalent to (x :: Bool) though
4

For a pure version:

{-# LANGUAGE BangPatterns #-}

while :: (a -> Bool) -> (a -> a) -> a -> a
while p f = go where go !x = if p x then go (f x) else x

test1 :: Int
test1 = while (< 1000) (* 2) 2
-- test1 => 1024

for monadic:

import Control.Monad

whileM :: (Monad m, MonadPlus f) => (a -> m Bool) -> m a -> m (f a)
whileM p f = go where
  go = do
    x <- f
    r <- p x
    if r then (return x `mplus`) `liftM` go else return mzero

test2 :: IO [String]
test2 = whileM (return . (/= "quit")) getLine
-- *Main> test2
-- quit
-- []
-- *Main> test2
-- 1
-- 2
-- 3
-- quit
-- ["1","2","3"]

power2 :: IO (Maybe Char)
power2 = whileM (return . (/= 'q')) getChar
-- *Main> power2
-- q
-- Nothing
-- *Main> power2
-- 1
-- 2
-- 3
-- q
-- Just '\n'

see also:

http://hackage.haskell.org/package/monad-loops, http://hackage.haskell.org/package/loop-while, http://hackage.haskell.org/package/control-monad-loop.

http://www.haskellforall.com/2012/01/haskell-for-c-programmers-for-loops.html

1 Comment

Another pure version) while p = until (not . p)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.