Reputation: 2011
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.
Upvotes: 2
Views: 501
Reputation: 477685
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 ()
.
Upvotes: 6
Reputation: 71119
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
Upvotes: 5