2
votes

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.

2
a :: IO t. Yes, it's polymorphic. An infinite computation never returns anything, so it can be any type.AJF
If the definition were a=a (an infinite recursion), the type would be a :: forall b. b, i.e. a could be of any type b. Since we have instead a = ioAction >> a, we force b to be of the form IO t, but t can be anything. So we get a :: forall t. IO t. It is still an infinite recursion, except that the IO action is run at every recursive call. In a sense, this works since >> won't evaluate its second argument/action before having executed its first argument/action.chi
fully parenthesized, what you have is a = (getLine >>= (putStrLn . filter isDigit)) >> a. (both >> and >>= are infixl 1).Will Ness
You say, "the first argument passed to >> would be of type IO String". What do you believe the first argument passed to >> is? Why do you believe it to have type IO String? (That is not the correct type for the first argument, so if you explain your thinking, we may help you spot an error in it.)Daniel Wagner

2 Answers

6
votes

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 ().

5
votes
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