我正在尝试在Haskell中进行一些编程。我正在尝试读取一个文件,然后使用line函数将文件中的每一行放在一个列表中。这是部分代码:
file = "muh.rtr"
readTrack :: String -> Track
readTrack file =
do let defFile = readFile file
let fileLines = lines defFile
但是,我一直收到这个错误:
Parser.hs:22:39:
Couldn't match expected type `String' with actual type `IO String'
In the first argument of `lines', namely `defFile'
In the expression: lines defFile
In an equation for `fileLines': fileLines = lines defFile
我一直在互联网上搜索几个小时,希望能在某处找到答案,但到目前为止我还没那么幸运。
答案 0 :(得分:9)
你可能想要这样的东西:
readTrack :: String -> IO Track
readTrack file = do defFile <- readFile file
let fileLines = lines defFile
-- etc....
......或类似的东西:
readTrack :: String -> IO Track
readTrack file = do fileLines <- liftM lines (readFile file)
-- etc....
但是你
将完全由非常简单的错误组成的代码提供给GHC,然后在Stack Overflow上发布错误消息不是一种好的学习方法。
答案 1 :(得分:6)
readFile
的类型是
readFile :: FilePath -> IO String
因此您需要使用<-
来绑定结果,并且您的函数必须返回IO Track
。
readTrack :: String -> IO Track
readTrack file =
do defFile <- readFile file
let fileLines = lines defFile
...
我建议在Haskell中阅读关于IO的好教程,例如Input and Output chapter of Learn You a Haskell for Great Good!。
答案 2 :(得分:5)
readFile返回IO string
。也就是说,它是一个返回字符串的IO计算。这意味着您需要使用<-
代替let
来“获取”其返回的字符串。
readTrack file =
do
defFile <- readFile file
...
您可以使用let
绑定非IO计算的内容,例如行的返回值,即常规字符串。
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
...
最后,您需要返回您可能想要尝试的值
readTrack file =
do
defFile <- readFile file
let fileLines = lines defFile
fileLines --Doesn't actually work!
但不幸的是,由于我们在“do”块中并且正在尝试返回monadic计算,我们需要将fileLines发送回io monad(记住,out函数返回IO [String]
,而不是{{ 1}}!
String
请注意,这里的“返回”是不是一个返回语句,在大多数语言中都可以找到,并且它不应该用在纯函数中。
这一切看起来好像很多。我建议你坚持使用纯函数(没有输入和输出/ monad),直到你得到更好的语言为止。
答案 3 :(得分:4)
你不能这样做 - 你已经遇到了IO monad。你需要做的是:
readTrack :: String -> IO Track
readTrack file = do
defFile <- readFile file
let fileLines = lines deffile
...
return whatever
将IO T
值视为语句(而不是表达式),返回类型为T
。因为语句有副作用,但表达式没有副作用,所以你永远不能将语句变成表达式;类型系统强制执行此操作,这就是您的类型签名不起作用的原因。
请注意do
块中不同的类似分配的语法:在此示例中,foo <- bar
用于IO
操作,而let baz = quux
语法用于纯粹的功能评估。使用monadic I / O会产生更多影响 - 它在Haskell的多态类型系统的完整通用性中更有意义,但是,对于纯粹的与副作用操作的语法指标,它也不一定是坏的。
通常,最好将大部分实现保留在纯函数域中:使用常规函数方法实现纯计算,然后在{{{I> O操作中描述您的I / O操作1}} monad。在IO monad中编写循环是一个常见的新手错误,它更适合作为列表推导或递归函数。
答案 4 :(得分:3)
如果您的函数应该具有类型readTrack :: String -> Track
,您确定String是文件名吗?也许是数据 - 如果是这样,请不要使用readFile
。写一些样本数据并使用它进行测试,例如
sampleData = "2 3\n1 30 234 45\n1 2 32 4\n5 3 4 23"
(关于这个家庭作业的另一个问题没有使用文件IO。我不会链接到它,因为你处于危机中并且可能想要复制,并且无论如何你拒绝学习haskell至少我会强迫你提高你的StackOverflow搜索技能!:))
在任何情况下,我认为通过解决字符串问题比通过解决IO问题获得更多分数。
延迟readFile问题,直到您的纯版本正常工作,否则您最终可能会在IO monad中编写大部分代码,这将比必要复杂得多。
你有一个纯函数readTrack :: String -> Track
,你可以做
readTrackFrom :: FilePath -> IO Track
readTrackFrom filename = fmap readTrack (readFile filename)
现在,fmap :: Functor f => (a -> b) -> f a -> f b
,所以采用纯函数并提升它们在不同的计算环境(如IO)中工作。
由于IO
是Functor
(明天查看,而不是今晚),我们将其用作(String -> Track) -> IO String -> IO Track
类型。这很好,因为readTrack :: String -> Track
和(readFile filename) :: IO String
。
如果您愿意,可以根据需要>>= print
或>>= writeFile newfilename
。
请勿忘记在使用deriving Show
后添加data Track =...
,但如果您使用的是type Track = ....
,则无需添加。{/ p>