views:

352

answers:

3

Hi, I have some troubles with Haskell type system.

Situation:

  • Following program is taking list of filenames on the cmdline
  • For all filename it reads its content using the function readFile
  • Content of each file is passed to inputParser (Parsec)
  • Rest is not so important
  • Main problem is in function read_modules
  • First two statements of the 'do' expression are invalid in Haskell type system
  • Problem is conflict between [String] x IO String x [Char] x ...
  • Function 'parse' should take String but when it gets it, it wants IO String suddenly (on the same argument), otherwise it wants String

What do I want:

  1. Read each file 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 GHC output errors

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:

  • 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 haskell greenie (not solving here)
  • TYPES

What are the questions:

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

Relevant weblinks:

Thank you all for your hints and comments.

+3  A: 

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.

Dave Hinton
+4  A: 

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.

svenningsson
Thank you for mentioning the IO. That was the major mistake.
Martin Kopta
+2  A: 

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.

camccann
Your answer isn't exactly what I wanted to hear, but after I have read something more about monads and <- operator I did understand many flaws in my code. Thank you for pointing to the true problem.
Martin Kopta
When it comes to simple functions in `IO`, it might help (for now) if you think about `<-` as "making it happen". Something with type `IO [String]` represents the idea of "doing some I/O and receiving a list of strings". Once you extract the value with `<-`, you've done the actual input and now you have your list, so you shouldn't be using another `<-` unless you want to be getting *more* input.
camccann
Though, as a caveat to my previous comment, that's definitely an oversimplification so don't take it too literally.
camccann