
On Fri, 7 Jun 2002, Chris Moline wrote:
... two. i have also read what the hell are monads and monads for the working haskell programmer. i still do not get what i am doing wrong.
getDepends :: String -> [String] getDepends p = do handle <- openFile (portsDir ++ p ++ "/+CONTENTS") ReadMode fetchDepends handle
to my brain this takes a string, concatenates it with portsDir and "+/CONTENTS", and passes it to openfile. openFile then returns a handle and this handle and passes it to fetchDepends. getDepends will return whatever fetchDepends returns, assuming openFile succeeds. however ghc says
Phoebe.hs:19: Couldn't match `[]' against `IO' Expected type: [t] Inferred type: IO Handle In the application `openFile (portsDir ++ (p ++ "/+CONTENTS")) ReadMode' In a 'do' expression pattern binding: handle <- openFile (portsDir ++ (p ++ "/+CONTENTS")) ReadMode
i do not know what this [t] is and i do not know why it is expected. my theory is it is being caused by something in fetchDepends.
[t] is a list type: GHC is expecting the call of openFile to return a list (of t, but t is just a type variable so tells us nothing). Why is it expecting a list? BECAUSE OF YOUR TYPE SIGNATURE! You wrote a type signature specifying that getDepends (and fetchDepends, for that matter), returns a list. You should have given the result an IO type, since these functions do IO. I haven't checked, but probably the type of getDepends should be getDepends :: String -> IO [String] rather than the type you wrote. So your mistake is just forgetting to include the monad in your type signature. Now, the error message you got maybe isn't the very clearest, but it is logical. Remember that the do syntax that you used in getDepends is OVERLOADED -- it can be used with any monad, not just with IO. In particular, it can be used with the list type, which is itself a monad. For example, we could, if we wished, define the ordinary list map function like this: map :: (a->b) -> [a] -> [b] map f xs = do x <- xs return (f x) That's an example of using do with the list monad. Of course, since the do is working over lists, then when we write x <- xs, the xs must also have a list type. We have to be consistent, and use the same monad throughout the do. This is just like when we use do to write an IO computation: in that case, when we write x <- f y or whatever, the f y has to have an IO type. Now, in your code for getDepends, GHC sees your type signature and says "Aha! This function returns a list. So the do in the body must be working over the list monad. In that case, when we see handle <- openFile... then the openFile must have a list type. Oh dear, it's type isn't a list, it's IO! Better complain that [] (the name of the list type, not the empty list) doesn't match IO!" Hence the error message you got. And now to a thorny and controversial question: should one write type signatures, or not? In particular, what advice should one give less experienced Haskell programmers? In this case, your CODE is probably quite correct. If you hadn't written the type signatures, then GHC would just have inferred the correct types and everything would have worked. Good advice for you might be to leave type signatures out, compile your code, and then look at the types that functions actually get (using ghci). You can always paste the types back into your code, and this way, they will be correct. On the other hand, if you omit type signatures, then when you DO get a type error it will be in terms of type variables and classes, rather than types such as Int or String which you would probably be expecting. There is a trade off here. One way to approach it is to write type signatures, but when a definition doesn't type check, remove the signature on that one and see whether it then typechecks. If so, the definition is right, but your type signature is wrong. Use ghci to find out the correct type signature, and insert it. You also mentioned layout problems when you used if in a do. I'm assuming you tried writing something like do .... if ... then ... else ... ... If you write this, then because the else appears in the same column as the if, it's taken to be the start of a new element in the do -- with a syntax error as the result. You just have to indent the else further than the if. I use an indentation like this: do ... if ... then ... else ... ... which lets GHC see that the entire if-then-else makes up just one element in the enclosing do. This is a classic gotcha of the layout rule: the first layout is quite natural, but you just can't write it.