开发者

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

I have some troubles with Haskell's type system.

Situation:

  • Following program is taking list of filenames on the command-line
  • For each filename its contents is read using the function readFile
  • Contents of each file is passed to inputParser (from Parsec library)
  • Rest is not so important
  • Main problem is in function read_modules
  • First two statements of the do expression are invalid in Haskell's type system
  • Problem is conflict between [String] vs IO String vs [Char] vs ...
  • Function parse should take a String but when it gets it, it wants an IO String suddenly (as the same argument), otherwise it wants a String

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 {
                                         Le开发者_运维百科ft 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:

  • I cannot understand what I should pass where - I kind of know what I want, but I don't get the syntax or the style.
  • I am new to Haskell.
  • Haskell's types...

What are the questions:

  • How do I fix the presented type issue?
  • What should I put into parse - what readFile gives me?
  • Are the types compatible?
  • Isn't there need for some type of conversion?

Relevant weblinks:

  • http://book.realworldhaskell.org/read/using-parsec.html
  • http://www.zvon.org/other/haskell/Outputprelude/readFile_f.html
  • http://www.haskell.org/pipermail/haskell/2002-November/010748.html

Thank you all for your hints and comments.


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜