Martin
Martin

Reputation: 1521

Why does Haskell's type system have a problem with my use of readFile?

I have some troubles with Haskell's type system.

Situation:

What do I want:

  1. Read each file's content
  2. Pass that content to the parse function as third argument

Here is the code:

module Main where

import System.IO
import System.Environment
import Text.ParserCombinators.Parsec
import InputParser
import Data

usage :: IO ()
usage = putStrLn "Usage: x file file file option"

parse_modules :: String -> [Char] -> Either ParseError [Module]
parse_modules filename input = parse inputParser filename input

read_modules :: [String] -> [Module]
read_modules [] = []::[Module]
read_modules (filename:rest) =
  do
    content <- readFile filename -- HERE is the problem
    modules <- case parse_modules filename content of -- HERE is problem too
      Left error -> do
        putStr "parse error at "
        print error
      Right out -> out ++ (read_modules rest)
    return modules

use :: [String] -> IO ()
use args =
  do
    init <- last args
    filenames <- take (length args - 1) args
    modules <- read_modules filenames
    return ()

main :: IO ()
main = do args <- getArgs
          if length args < 2
            then usage
            else use args

Here are the errors GHC outputs:

ghc --make -o x.hs input-parser.hs data.hs
[3 of 3] Compiling Main             ( x.hs, x.o )

x.hs:19:4:
    Couldn't match expected type `IO String'
           against inferred type `[String]'
    In a stmt of a 'do' expression: content <- readFile filename
    In the expression:
        do content <- readFile filename
           modules <- case parse_modules filename content of {
                        Left error -> do ...
                        Right out -> out ++ (read_modules rest) }
           return modules
    In the definition of `read_modules':
        read_modules (filename : rest)
                       = do content <- readFile filename
                            modules <- case parse_modules filename content of {
                                         Left error -> ...
                                         Right out -> out ++ (read_modules rest) }
                            return modules
-- THIS ERROR is somewhat not important
x.hs:30:4:
    Couldn't match expected type `[Char]'
           against inferred type `IO Char'
      Expected type: String
      Inferred type: IO Char
    In a stmt of a 'do' expression: init <- last args
    In the expression:
        do init <- last args
           filenames <- take (length args - 1) args
           modules <- read_modules filenames
           return ()
make: *** [x] Error 1

What is the problem:

What are the questions:

Relevant weblinks:

Thank you all for your hints and comments.

Upvotes: 1

Views: 1823

Answers (3)

C. A. McCann
C. A. McCann

Reputation: 77414

Here's what's causing the other error that you said was less important:

use :: [String] -> IO ()
use args =
  do
    init <- last args

The <- operator is used within a do block to extract something contained in a monad (in this case, IO) so that you can work with the actual value trapped inside. But, args here is of type [String], not IO [String], so you don't need to do that; you already pulled the argument list out of IO with arg <- getArgs in main.

If you want to assign a non-monadic value to a temporary variable inside a do block, use let instead, like this:

let x = last args

It looks like you're making the same mistake in several other places as well, not just that line. Having to treat monadic vs. non-monadic values differently like that, when you just want to make a temporary variable inside your function, is an easy thing to get confused about for someone new to the language.

By the way, init is the name of a function in the standard library, so you might want to use a different variable name.

Upvotes: 2

svenningsson
svenningsson

Reputation: 4049

First of all, since your function read_modules performs I/O it must return something of type IO. That means that you have to change a number of things in your function:

  1. The empty case must use return
  2. The Right branch in the case expression must use do-notation
  3. When calling itself recursively the function must do so within the do-notation

Here's a (hopefully) fixed version of your read_modules function:

read_modules :: [String] -> IO [Module]
read_modules [] = return []
read_modules (filename:rest) =
  do
    content <- readFile filename -- HERE is the problem
    modules <- case parse_modules filename content of -- HERE is problem too
      Left error -> do
        putStr "parse error at "
        print error
      Right out -> do 
        more <- read_modules rest
        return (out ++ more)
    return modules

I haven't tested it but I hope it will help you on the way.

Upvotes: 4

dave4420
dave4420

Reputation: 47062

This is wrong.

read_modules :: [String] -> [Module]

Should be

read_modules :: [String] -> IO [Module]

That is not all that you need to fix, but it will get you going.

Upvotes: 3

Related Questions