Simple example on do syntax transformed to >>= syntax - haskell

How can reverse2lines transformed into the using >>= syntax ? Just like addOneInt is transformed into addOneInt'?
addOneInt :: IO ()
addOneInt = do line <- getLine
putStrLn (show (1 + read line :: Int))
addOneInt' :: IO ()
addOneInt' = getLine >>= \line ->
putStrLn (show ( 1 + read line :: Int))
reverse2lines :: IO ()
reverse2lines =
do line1 <- getLine
line2 <- getLine
putStrLn (reverse line2)
putStrLn (reverse line1)
Please also consider reading the follow up question and its valuable answers.

You could safely deduce it from what you already know. Start by fully parenthesizing your addOneInt':
addOneInt' = getLine >>= (\line ->
putStrLn (show (1 + read line :: Int)) )
Next, you reverse2lines can be equivalently written as
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
do { putStrLn (reverse line2) ;
do { putStrLn (reverse line1) } } } }
Applying the one-step transformation that you already used for addOneInt, as much times as needed, you'd get
reverse2lines' :: IO ()
reverse2lines' =
getLine >>= (\line1 ->
getLine >>= (\line2 ->
putStrLn (reverse line2) >>
putStrLn (reverse line1) ) )
Syntactically, the parentheses around lambda expressions can be omitted, but writing them explicitly here helps to clarify and make obvious the nested structure of these functions, especially if we'd line up the indentation.
Full translation arranges for the monad's fail function to be called on pattern mismatch, but since the variable patterns (line1, line2) are irrefutable, this translation is in fact exact.

Statements of type x <- m can be translated as m >>= \x -> ..., and statements in which the return value isn't bound like m can be translated as m >> ...
reverse2lines :: IO ()
reverse2lines = getLine
>>= \line1 -> getLine
>>= \line2 -> putStrLn (reverse line2)
>> putStrLn (reverse line1)
The translation isn't exact - the fail monad function complicates things a little. You can read about it here, but as far as I know it's irrelevant when dealing with the IO monad.

Here is the code from Will's very nice answer using only >>= :
reverse2lines' :: IO ()
reverse2lines' =
getLine >>= (\line1 ->
getLine >>= (\line2 ->
putStrLn (reverse line2) >>= (\_ ->
putStrLn (reverse line1) )))
Lesson learned : the sequencing in do corresponds simply to nesting of lambda expressions.

Related

Haskell - binding a mapped monadic function

Within this block of code here, I have a function tagsort that takes a FilePath and returns an IO String.
builddir xs = do
writeto <- lastest getArgs
let folderl b = searchable <$> (getPermissions b)
let filel c = ((lastlookup mlookup c) &&) <$> ((not <$> folderl c))
a <- listDirectory xs
listdirs <- filterM (folderl) (map ((xs ++ "/") ++) a)
filedirs <- filterM (filel) (map ((xs ++ "/") ++) a)
tagfiles <- tagsort <$> filedirs
--testprint to terminal
putStrLn $ concat listdirs
putStrLn $ concat tagfiles
tagsort :: Control.Monad.IO.Class.MonadIO m => FilePath -> m [Char]
tagsort xs = do
nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
let artist = init $ drop 8 $ show nsartist
let album = init $ drop 7 $ show nsalbum
pure (artist ++ " - " ++ album)
I'd like to take this function and map it over a list of directories. When run, I get this error.
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO (t0 [Char])
Actual type: [t0 [Char]]
• In a stmt of a 'do' block: tagfiles <- tagsort <$> filedirs
I believe I understand what is happening here. In order to bind in the way I desire to tagfiles, I would want an IO [String], but instead mapping tagsort to the list filedirs produces [IO String]. I am not totally sure how to circumvent this or if it is even able to be circumvented at all. Perhaps mapping is not the correct way to do this? Any help would be appreciated.
This is because the function tagsort is of type String -> IO String
Note: I am using IO for simplicity and String for both [Char] and FilePath.
However, when mapping it onto filedir :: [String] using
(<$>) = fmap :: Functor f => (a -> b) -> f a -> f b
a conflict occured between IO [String] and `[IO String] - the former is what the compiler expects an expression in a do block to be.
This isn't very useful on first glance. However, haskell has a function called sequence for this exact task. Its constraints needn't matter much now, as Foldable is something completely different. For now, know that it can be of type [IO a] -> IO [a]
Luckily again, there's a very useful predefined utility function, mapM just for sequence . map f.
The final code will be:
mapM tagSort fileDirs

putStr inside IO () function

How to call IO () function inside another IO () function? I want to print to standard output then call function to do the same.
For example,
p :: String -> IO ()
p [x] = putStr x
p xs = q xs
q :: String -> IO ()
q (x:xs) = putStr x ++ p xs
Your first problem is with typing
p [x] = putStr x
{- putStr :: String -> IO ()
x :: Char, not a String
-}
and
q (x:xs) = putStr x ++ p xs
{- (++) :: [a] -> [a] -> [a]
putStr x :: IO (), not a list of anything.
-}
Let's look at q first, since it follows from p. You're breaking it down into characters, so you should use putChar rather than putStr
Also we're looking at sequencing actions, so we should either use (>>) or (>>=) depending on whether or not you need the result. In this case the result is a value of the unit type (()) which is a useless result and safe to ignore.
q :: String -> IO ()
q (x:xs) = putChar x >> p xs
{- or using `do` notation:
q (x:xs) = do
putChar x
p xs
-}
p can be changed likewise to use putChar rather than putStr
p :: String -> IO ()
p [x] = putChar x
p xs = q xs
though be aware that you haven't matched an empty list on either p or q.
About this time you should notice that substituting putChar for putStr just so you can break strings down to Chars is kind of backward thinking. p = putStr and you're done. However, if you're committed to this backward thinking:
import Control.Monad (foldM_, mapM_)
p = foldM_ (\_ x -> putChar x) ()
-- or
p = foldM_ ((putChar .) . flip const) ()
-- or
p = mapM_ putChar

Combining two readMaybe and getLine in haskell

In the haskell wikibook, one is asked to write something like this:
adding = do
putStrLn "enter first number"
first <- readMaybe <$> getLine
putStrLn "enter second number"
second <- readMaybe <$> getLine
let x = (+) <$> first <*> second :: Maybe Double
case x of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do
putStrLn "not good"
adding
This asks for two numbers and then adds them. However I tried to write it a little bit shorter, doing both getlines on the same line:
adding2 = do
putStrLn "enter two numbers"
x <- (+) <$> (readMaybe <$> getLine) <*> (readMaybe <$> getLine)
case x of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do
putStrLn "not good"
adding
but this doesn't work. It complains about the type of d, and I suspect it is because I haven't said anywhere that it should be a Maybe Double.
Or maybe it is the line (+) <$> (readMaybe <$> getLine) <*> (readMaybe <$> getLine)?
How can I fix this?
Apparently the problem is that I have two different monads and can't do what I'm hoping to do. I wrote one variant that I'm particularly happy with:
adding5 :: IO ()
adding5 = do
putStrLn "enter two numbers"
let a = readMaybe <$> getLine
a >>= \ a1 -> a >>= \ a2 ->
case (+) <$> a1 <*> a2 of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do putStrLn "not good"
adding5
Here is another one following amalloy's suggestion in the comments:
adding6 = do
putStrLn "enter two numbers"
[a1, a2] <- replicateM 2 (readMaybe <$> getLine)
case (+) <$> a1 <*> a2 of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do putStrLn "not good"
adding6
In the original function, you had
first :: Maybe Double
second :: Maybe Double
So when you combined them with <$> and <*> you were operating in a Maybe context: exactly what you wanted.
But in your new function, your second argument to <*> is
readMaybe <$> getLine :: Read t => IO (Maybe t)
You're now using <$> and <*> in the IO context, not the Maybe context, and so the things you are trying to add are of type Maybe t, not t. You can't add values of type Maybe t no matter what t is, because Maybe is not Num. So even if you give the compiler enough information to conclude that t should be Double, this won't work.
You have to either bind the IO values to local unwrapped names, as you did in your first function, or, equivalently, use >>= to operate inside of the IO (Maybe Double) you have.

Adding an additional step to ">>="

I have this which works fine:
forM_ [1..10] $ \x -> myFunc1 x 99 >>= putStrLn . show >> return ()
myFunc1 :: Int -> Int -> IO Bool
myFunc1 .....
I want to do add an additional string to the output:
forM_ [1..10] $ \x -> myFunc1 x 99 >>= (++) "the result is: " >>= putStrLn . show >> return ()
But that doesn't compile. I've tried different variations but still no success. Your suggestions?
The first thing that sticks out is that the expression:
(++) "the result is: "
is not an IO-action - it's just a pure function String -> String, and
that's one reason why you code isn't type checking.
To turn it into an IO-action you can compose it with return:
return . ( (++) "the result is: " )
:: String -> IO String
Now you can use it with >>=.
However, that's not where the real problem is...
To prepend "the result is: " before the show you have to insert it in the putStrLn call:
... >>= putStrLn . (++ "the result is: ") . show
(Note that there is no need to >> return () since putStrLn already returns ()).
Honestly, this is a lot simpler using do-notation:
forM_ [1..10] $ \x -> do
s <- myFunc1 x 99
putStrLn $ "the result is: " ++ show s
You can compose show with the concatenation function like so:
forM_ [1..10] $ \x -> myFunc1 x 99 >>= putStrLn . ("the result is: " ++) . show >> return ()
Replace ++ with add
add x y = return (x ++ show (y))
forM_ [1..10] $ \x -> myFunc1 x 99 >>= add "the result is: " >>= putStrLn . show >> return ()
reason: The return type of ++ is not IO type

How to set getLine to only receive strings that can be converted to numbers?

main = do
putStrLn "Hello,Name please?"
first <- getLine
second <- getLine
third <- getLine
if (second == "divide") then putStrLn (show (read first ::Double )/ (read third :: Double))
else putStrLn "Cannot do"
So i want a number in first and third variables,and the second variable will be given the work divide and the if statement will begin and convert the String in first to a Double.But i think the issue is that the variable First is expecting a String like "one" that cannot be converted to a Double.How to fix this to allow getLine to only receive Strings that can be changed in numbers
Here's a quick idea that could help you:
import Control.Applicative
import Data.Char
getNum :: (Num a, Read a) => IO (Maybe a)
getNum = do
(x, xs) <- break (not . isDigit ) <$> getLine
case xs of
[] -> return $ Just $ read x
_ -> return Nothing
main :: IO ()
main = do
x <- getNum :: IO (Maybe Double)
case x of
Nothing -> do
putStrLn "Not a number, try again"
main
Just x' ->
putStrLn $ "Your number: " ++ show x'
return ()
You cannot "make" getLine accept only numbers, you make a function that does. You can even modify this function to accept only the first set of numbers before a non-digit (right now it returns Nothing if any non-digit is in the string).
Update
Here's a program that asks for two numbers and an operation, doing some value checking:
import Control.Applicative
import Data.Char
import Data.Maybe
getNum :: (Num a, Read a) => IO (Maybe a)
getNum = do
(x, xs) <- break (not . isDigit ) <$> getLine
case xs of
[] -> return $ Just $ read x
_ -> return Nothing
main :: IO ()
main = do
putStr "First: "
x <- getNum :: IO (Maybe Double)
putStr "Operation: "
op <- getLine
putStr "Second: "
y <- getNum :: IO (Maybe Double)
if isJust x && isJust y then do
let
x' = fromJust x
y' = fromJust y
putStr "Result: "
case op of
"divide" -> putStrLn $ show $ x' / y'
"sum" -> putStrLn $ show $ x' + y'
_ -> putStrLn "No operation"
else
putStrLn "Invalid numbers"
return ()

Resources