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]
vsIO String
vs[Char]
vs ... - Function
parse
should take aString
but when it gets it, it wants anIO String
suddenly (as the same argument), otherwise it wants aString
What do I want:
- Read each file's content
- 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
- whatreadFile
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_module
s performs I/O it must return something of type IO
. That means that you have to change a number of things in your function:
- The empty case must use
return
- The
Right
branch in the case expression must use do-notation - 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.
精彩评论