I have the following Haskell expression:
a = getLine >>= putStrLn . filter isDigit >> a
I am having trouble understanding how the above expression works. I know the >>= function takes a monadic value and a function (that takes a normal value and returns a monadic value), and returns a monadic value.
I know that getLine and putStrLn have the following type declarations:
getLine :: IO String
putStrLn :: String -> IO ()
So the following part of expression:
a = getLine >>= putStrLn . filter isDigit
Would return an IO (). However, the function >> takes a first monadic value and a second monadic value and returns the second monadic value.
Given the original expression, the first argument passed to >> would be of type IO String. The second argument is a.
My question is, what is the type of a, and how does the above expression work to continually take user input and print only the numeric part of the input back to the screen? Any insights are appreciated.
Note: I renamed the a function to readPrintLoop as suggested by #SamuelBarr, since that avoids some confusion.
My question is, what is the type of readPrintLoop, and how does the above expression work to continually take user input and print only the numeric part of the input back to the screen?
readPrintLoop has type: readPrintLoop :: IO a so it is an IO. The a can be any type, since we never "return" that value, we will never end this function.
The function is constantly repeated because readPrintLoop is defined in terms of itself. readPrintLoop is defined as:
readPrintLoop :: IO a
readPrintLoop = getLine >>= putStrLn . filter isDigit >> readPrintLoop
We here thus have infinite recursion, since eventually you will run into a, and thus replace that with another getLine >>= putStrLn . filter isDigit >> a and so on.
However, the function >> takes a first monadic value and a second monadic value and returns the second monadic value.
(>>) is equivalent to:
(>>) :: Monad m => m a -> m b -> m b
u >> v = u >>= (\_ -> v)
so the implementation of a is equivalent to:
readPrintLoop :: IO a
readPrintLoop = getLine >>= putStrLn . filter isDigit >>= \_ -> readPrintLoop
Here the underscore variable _ will be passed ().
a = getLine >>= putStrLn . filter isDigit
is not "the part of expression".
getLine >>= putStrLn . filter isDigit
is the part of the expression. And it does not "return IO ()". It has the type IO () (which you've correctly inferred (*)). It is a "monadic value" that you talk about.
Giving it a name, any name,
ioAction :: IO ()
ioAction = getLine >>= (putStrLn . filter isDigit)
we end up with
a = ioAction >> a
----------------------------------
(>>) :: IO a -> IO b -> IO b
ioAction :: IO ()
a :: IO b
----------------------------------
a :: IO b
and everything typechecks.
The semantics of a in
a = ((>>) ioAction) a
is defined by the semantics of >>.
(*)
----------------------------------------------------
(>>=) :: m a -> (a -> m b) -> m b
getLine :: IO String m a
putStrLn :: String -> IO ()
putStrLn . filter isDigit :: String -> IO () a -> m b
---------------------------------------------------- ------------
getLine >>= (putStrLn . filter isDigit) :: IO () m b
Related
How can I use pure functions inside IO functions? :-/
For example: I'm reading a file (IO function) and I want to parse its context, a string, by using a pure function with referential transparency.
It seems such worlds, pure functions and IO functions, are separated. How can I possibly bridge them?
The simplest way is to use fmap, which has the following type:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO implements Functor, which means that we can specialize the above type by substituting IO for f to get:
fmap :: (a -> b) -> IO a -> IO b
In other words, we take some function that converts as to bs, and use that to change the result of an IO action. For example:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
What just happened there? Well, map toUpper has type:
map toUpper :: String -> String
It takes a String as an argument, and returns a String as a result. Specifically, it uppercases the entire string.
Now, let's look at the type of fmap (map toUpper):
fmap (map toUpper) :: IO String -> IO String
We've upgraded our function to work on IO values. It transforms the result of an IO action to return an upper-cased string.
We can also implement this using do notation, to:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
It turns out that every monad has the following property:
fmap f m = do
x <- m
return (f x)
In other words, if any type implements Monad, then it should always be able to implement Functor, too, using the above definition. In fact, we can always use the liftM as the default implementation of fmap:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM is identical to fmap, except specialized to monads, which are not as general as functors.
So if you want to transform the result of an IO action, you can either use:
fmap,
liftM, or
do notation
It's really up to you which one you prefer. I personally recommend fmap.
You can also consider liftM function from Control.Monad.
A little example to help you (run it into ghci as you are under the IO Monad)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
The actual answer is as follows:
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return wraps it in the appropriate monad so that do can assign it to val.
Alex Horsman helped me. He said:
"Perhaps I'm misunderstanding, but that sounds pretty simple?
do {x <- ioFunc; return (pureFunc x)}"
And then I solved my problem:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}
I try to understand the lifting principle by example, and found this:
https://github.com/graninas/Functional-Design-and-Architecture/blob/1736abc16d3e4917fc466010dcc182746af2fd0e/First-Edition/BookSamples/CH03/MonadStack.hs
Then, if I change it's lift (lift (putStrLn "bla-bla")) to putStrLn "bla-bla", the compiler throw error!
I do this base on my understanding: do block are just syntax sugar, and each line's result are pass into next line. If the next line do not use the args that passed from previous line, the type of args won't cause type confliction, I think.
Take bellow as example, while x<- getLine can past compiling
test:: IO ()
test = do
x <- getLine -- discarded and compiler don't care it type
let a = "bla-bla" -- discarded and compiler don't care it type
putStrLn $ "You type are discarded: "
Now back to the calculations function:
type Data = Map.Map Int String
type StateIO = StateT Data IO
type MaybeStateIO a = MaybeT StateIO a
calculations :: MaybeStateIO ()
calculations = do
lift (lift (putStrLn "bla-bla")) -- if I change this to `putStrLn "bla-bla"`, it failed compiling.
lift (modify (Map.insert 3 "3"))
lift (modify (Map.insert 1 "1"))
mb <- lift (get >>= (return . Map.lookup 1))
lift (lift (print mb))
main = runStateT (runMaybeT calculations) Map.empty
I don't understand is that to compiler require lifting on putStrLn "bla-bla".
Isn't it enough when return value of the last line of do block match the function's return value?
In this example, how does the compiler decide the do block's value type? according to function's signature?
Can anyone explains the lift for me? How do it work, when to use, etc.
Isn't it enough when return value of the last line of do block match the function's return value?
No, since that would mean that you could write a do block where the first item for example would use the instance of [] for Monad whereas the next would use for example Maybe or IO, but then how would x <- some_list make sense for a list of putStrLn x? All lines in the do block should be of type m a with m the same instance of Monad, and the as can have different types for each line. If you write a do block with:
foo = do
x <- exp1
exp2
then this is translated to exp1 >>= \x -> exp2, and since (>>=) :: Monad m => m a -> (a -> m b) -> m b operates where the two operands share the same monad m, this thus means that exp1 :: m a and exp2 :: m b thus need to work with the same monadic type m.
You require to perform lifting twice since the line should have as type MaybeT (StateT Data IO) a whereas putStrLn "bla-bla" has IO a, it thus requires one lift :: (MonadTrans t, Monad m) => m a -> t m a to lift it to StateT Data IO a and another to finally lift it to a MaybeT (StateT Data IO) a.
The value of calculations is a MaybeStateIO value. That's the monad you are operating in, so that's what every line of the do block has to produce. But putStrLn "bla-bla" does not produce a MaybeStateIO value; it just produces an IO value. The first lift takes that IO value and returns a StateIO value; the second lift takes that StateIO value and returns a MaybeStateIO value.
Remember,
do
a
b
is just syntactic sugar for a >> b, and (>>) :: Monad m => m a -> m b -> m b needs values from the same monad as arguments. It's only the "return value" (a and b) of the monad that can vary from line to line; the monad m itself is fixed.
I have a function that calls functions of type IO and functions of type Either String a.
I would like to combine the effects in the do notation, so that I can unpack IO when necessary and at the same time abort the computation at the first Left I encounter.
Here is a very simple example, can you help me fix it? (use of runEitherT is optional. But I think if you use plain either you won't be able to use MonadError and MonadIO features).
entryPoint :: IO (Either String Int)
entryPoint = runEitherT foo
-- p and p' should have type Int,
-- and errorf can force the computation to abort (as can throwError)
foo :: EitherT String IO Int
foo = do
p <- liftIO $ iof 1
p' <- return $ errorf p
if p' == 2
then throwError "No!"
else return 1
errorf :: b -> Either String b
errorf = undefined
iof :: a -> IO a
iof = undefined
The part
p' <- return $ errorf p
looks wrong. Here, return builds a monadic value in EitherT String IO. Assuming that errorf p = Left something the value which is being built is
p' <- EitherT (return (Right (Left something)))
where the above return builds an IO action. This is not what we want -- there is an extra Right there. We instead want
p' <- EitherT (return (Left something))
which is
p' <- EitherT $ return $ errorf p
How can I use pure functions inside IO functions? :-/
For example: I'm reading a file (IO function) and I want to parse its context, a string, by using a pure function with referential transparency.
It seems such worlds, pure functions and IO functions, are separated. How can I possibly bridge them?
The simplest way is to use fmap, which has the following type:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO implements Functor, which means that we can specialize the above type by substituting IO for f to get:
fmap :: (a -> b) -> IO a -> IO b
In other words, we take some function that converts as to bs, and use that to change the result of an IO action. For example:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
What just happened there? Well, map toUpper has type:
map toUpper :: String -> String
It takes a String as an argument, and returns a String as a result. Specifically, it uppercases the entire string.
Now, let's look at the type of fmap (map toUpper):
fmap (map toUpper) :: IO String -> IO String
We've upgraded our function to work on IO values. It transforms the result of an IO action to return an upper-cased string.
We can also implement this using do notation, to:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
It turns out that every monad has the following property:
fmap f m = do
x <- m
return (f x)
In other words, if any type implements Monad, then it should always be able to implement Functor, too, using the above definition. In fact, we can always use the liftM as the default implementation of fmap:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM is identical to fmap, except specialized to monads, which are not as general as functors.
So if you want to transform the result of an IO action, you can either use:
fmap,
liftM, or
do notation
It's really up to you which one you prefer. I personally recommend fmap.
You can also consider liftM function from Control.Monad.
A little example to help you (run it into ghci as you are under the IO Monad)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
The actual answer is as follows:
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return wraps it in the appropriate monad so that do can assign it to val.
Alex Horsman helped me. He said:
"Perhaps I'm misunderstanding, but that sounds pretty simple?
do {x <- ioFunc; return (pureFunc x)}"
And then I solved my problem:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}
I was playing around with composable failures and managed to write a function with the signature
getPerson :: IO (Maybe Person)
where a Person is:
data Person = Person String Int deriving Show
It works and I've written it in the do-notation as follows:
import Control.Applicative
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
where
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
I wrote this function with the intent of creating composable possible failures. Although I've little experience with monads other than Maybe and IO this seems like if I had a more complicated data type with many more fields, chaining computations wouldn't be complicated.
My question is how would I rewrite this without do-notation? Since I can't bind values to names like name or age I'm not really sure where to start.
The reason for asking is simply to improve my understanding of (>>=) and (<*>) and composing failures and successes (not to riddle my code with illegible one-liners).
Edit: I think I should clarify, "how should I rewrite getPerson without do-notation", I don't care about the getInt function half as much.
Do-notation desugars to (>>=) syntax in this manner:
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
getPerson2 =
getLine >>=
( \name -> getInt >>=
( \age -> return $ Just Person <*> Just name <*> age ))
each line in do-notation, after the first, is translated into a lambda which is then bound to the previous line. It's a completely mechanical process to bind values to names. I don't see how using do-notation or not would affect composability at all; it's strictly a matter of syntax.
Your other function is similar:
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt2 :: IO (Maybe Int)
getInt2 =
(fmap reads getLine :: IO [(Int,String)]) >>=
\n -> case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
A few pointers for the direction you seem to be headed:
When using Control.Applicative, it's often useful to use <$> to lift pure functions into the monad. There's a good opportunity for this in the last line:
Just Person <*> Just name <*> age
becomes
Person <$> Just name <*> age
Also, you should look into monad transformers. The mtl package is most widespread because it comes with the Haskell Platform, but there are other options. Monad transformers allow you to create a new monad with combined behavior of the underlying monads. In this case, you're using functions with the type IO (Maybe a). The mtl (actually a base library, transformers) defines
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
This is the same as the type you're using, with the m variable instantiated at IO. This means you can write:
getPerson3 :: MaybeT IO Person
getPerson3 = Person <$> lift getLine <*> getInt3
getInt3 :: MaybeT IO Int
getInt3 = MaybeT $ do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt3 is exactly the same except for the MaybeT constructor. Basically, any time you have an m (Maybe a) you can wrap it in MaybeT to create a MaybeT m a. This gains simpler composability, as you can see by the new definition of getPerson3. That function doesn't worry about failure at all because it's all handled by the MaybeT plumbing. The one remaining piece is getLine, which is just an IO String. This is lifted into the MaybeT monad by the function lift.
Edit
newacct's comment suggests that I should provide a pattern matching example as well; it's basically the same with one important exception. Consider this example (the list monad is the monad we're interested in, Maybe is just there for pattern matching):
f :: Num b => [Maybe b] -> [b]
f x = do
Just n <- x
[n+1]
-- first attempt at desugaring f
g :: Num b => [Maybe b] -> [b]
g x = x >>= \(Just n) -> [n+1]
Here g does exactly the same thing as f, but what if the pattern match fails?
Prelude> f [Nothing]
[]
Prelude> g [Nothing]
*** Exception: <interactive>:1:17-34: Non-exhaustive patterns in lambda
What's going on? This particular case is the reason for one of the biggest warts (IMO) in Haskell, the Monad class's fail method. In do-notation, when a pattern match fails fail is called. An actual translation would be closer to:
g' :: Num b => [Maybe b] -> [b]
g' x = x >>= \x' -> case x' of
Just n -> [n+1]
_ -> fail "pattern match exception"
now we have
Prelude> g' [Nothing]
[]
fails usefulness depends on the monad. For lists, it's incredibly useful, basically making pattern matching work in list comprehensions. It's also very good in the Maybe monad, since a pattern match error would lead to a failed computation, which is exactly when Maybe should be Nothing. For IO, perhaps not so much, as it simply throws a user error exception via error.
That's the full story.
do-blocks of the form var <- e1; e2 desugar to expressions using >>= as follows e1 >>= \var -> e2. So your getPerson code becomes:
getPerson =
getLine >>= \name ->
getInt >>= \age ->
return $ Just Person <*> Just name <*> age
As you see this is not very different from the code using do.
Actually, according to this explaination, the exact translation of your code is
getPerson =
let f1 name =
let f2 age = return $ Just Person <*> Just name <*> age
f2 _ = fail "Invalid age"
in getInt >>= f2
f1 _ = fail "Invalid name"
in getLine >>= f1
getInt =
let f1 n = case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
f1 _ = fail "Invalid n"
in (fmap reads getLine :: IO [(Int,String)]) >>= f1
And the pattern match example
f x = do
Just n <- x
[n+1]
translated to
f x =
let f1 Just n = [n+1]
f1 _ = fail "Not Just n"
in x >>= f1
Obviously, this translated result is less readable than the lambda version, but it works with or without pattern matching.