简体   繁体   English

为什么 Haskell 的类型系统在我使用 readFile 时有问题?

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

I have some troubles with Haskell's type system.我对 Haskell 的类型系统有一些麻烦。

Situation:情况:

  • Following program is taking list of filenames on the command-line以下程序正在命令行上获取文件名列表
  • For each filename its contents is read using the function readFile对于每个文件名,使用函数readFile读取其内容
  • Contents of each file is passed to inputParser (from Parsec library)每个文件的内容传递给inputParser (来自Parsec库)
  • Rest is not so important休息没那么重要
  • Main problem is in function read_modules主要问题在于函数read_modules
  • First two statements of the do expression are invalid in Haskell's type system do表达式的前两个语句在 Haskell 的类型系统中无效
  • Problem is conflict between [String] vs IO String vs [Char] vs ...问题是[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函数parse应该接受一个String但是当它得到它时,它突然想要一个IO String (作为相同的参数),否则它想要一个String

What do I want:我想要什么:

  1. Read each file's content读取每个文件的内容
  2. Pass that content to the parse function as third argument将该内容作为第三个参数传递给parse函数

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 输出的错误:

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 new to Haskell.我是 Haskell 的新手。
  • Haskell's types... Haskell 的类型...

What are the questions:有哪些问题:

  • How do I fix the presented type issue?如何解决出现的类型问题?
  • What should I put into parse - what readFile gives me?我应该在parse放入什么 - readFile给了我什么?
  • Are the types compatible?类型是否兼容?
  • Isn't there need for some type of conversion?不需要某种类型的转换吗?

Relevant weblinks:相关网页链接:

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 .首先,由于您的函数read_module s 执行 I/O,它必须返回IO类型的东西。 That means that you have to change a number of things in your function:这意味着您必须更改函数中的许多内容:

  1. The empty case must use return空的情况必须使用return
  2. The Right branch in the case expression must use do-notation case 表达式中的Right分支必须使用 do-notation
  3. When calling itself recursively the function must do so within the do-notation当递归调用自身时,函数必须在 do-notation 中调用

Here's a (hopefully) fixed version of your read_modules function:这是您的read_modules函数的(希望)固定版本:

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. <-运算符用于do块中以提取包含在 monad(在本例中为IO )中的内容,以便您可以使用困在其中的实际值。 But, args here is of type [String] , not IO [String] , so you don't need to do that;但是,这里的args[String]类型,而不是IO [String] ,所以你不需要这样做; you already pulled the argument list out of IO with arg <- getArgs in main.您已经使用arg <- getArgs在 main 中从IO提取了参数列表。

If you want to assign a non-monadic value to a temporary variable inside a do block, use let instead, like this:如果你想给do块内的临时变量分配一个非 monadic 值,请改用let ,如下所示:

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.当你只想在你的函数中创建一个临时变量时,必须以不同的方式对待 monadic 和非 monadic 值,对于语言的新手来说很容易混淆。

By the way, init is the name of a function in the standard library, so you might want to use a different variable name.顺便说一下, init是标准库中函数的名称,因此您可能需要使用不同的变量名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM