[英]Haskell IO: convert IO String to “Other type”
I have a Haskell program which takes a file as an input and convert it into a binary search tree. 我有一个Haskell程序,它将文件作为输入并将其转换为二叉搜索树。
import System.IO
data Tree a = EmptyBST | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
ins :: Ord a => a -> (Tree a) -> (Tree a)
ins a EmptyBST = Node a EmptyBST EmptyBST
ins a (Node p left right)
| a < p = Node p (ins a left) right
| a > p = Node p left (ins a right)
| otherwise = Node p left right
lstToTree :: Ord a => [a] -> (Tree a)
lstToTree = foldr ins EmptyBST
fileRead = do file <- readFile "tree.txt"
let a = lstToTree (conv (words file))
return a
conv :: [String] -> [Int]
conv = map read
However, when I run the following command: 但是,当我运行以下命令时:
ins 5 fileRead
I got the following error: 我收到以下错误:
<interactive>:2:7:
Couldn't match expected type `Tree a0'
with actual type `IO (Tree Int)'
In the second argument of `ins', namely `fileRead'
In the expression: ins 5 fileRead
In an equation for `it': it = ins 5 fileRead
Please does anyone can help me? 请有人帮我吗?
Thanks 谢谢
You'd be able to see the problem right away if you supplied fileRead
with a type signature. 如果您提供带有类型签名的
fileRead
,您将能够立即看到问题。 Let's figure out the type annotation that GHC will internally assign to fileRead
: 让我们弄清楚GHC将在内部分配给
fileRead
的类型注释:
fileRead = do file <- readFile "tree.txt"
let t = lstToTree $ map read $ words file
return t
lstToTree :: Ord a => [a] -> Tree a
, and read
always returns a member of the Read
typeclass. lstToTree :: Ord a => [a] -> Tree a
, read
总是返回Read
类型类的成员。 So t :: (Read a, Ord a) => Tree a
. 所以
t :: (Read a, Ord a) => Tree a
。 The concrete type depends on the contents of the file. 具体类型取决于文件的内容。
return
wraps its argument in a monad, so return t
has the type Ord a, Read a => IO (Tree a)
. return
将其参数包装在monad中,因此return t
的类型为Ord a, Read a => IO (Tree a)
。 Since return t
is the final statement in the do
block, it becomes the return type of fileRead
, so 由于
return t
是do
块中的最后一个语句,因此它成为fileRead
的返回类型,所以
fileRead :: (Read a, Ord a) => IO (Tree a)
So fileRead
is a Tree
wrapped in an IO
, and you can't pass it directly into ins
because it expects a Tree
on its own. 所以
fileRead
是一个包含在IO
的Tree
,你不能直接将它传递给ins
因为它需要一个Tree
自己。 You can't take the Tree
out of the IO
, but you can 'lift' the function ins
into the IO
monad. 您无法将
Tree
从IO
取出,但您可以将函数ins
“提升”到IO
monad中。
Control.Monad exports liftM :: Monad m => (a -> r) -> (ma -> mr)
. Control.Monad导出
liftM :: Monad m => (a -> r) -> (ma -> mr)
。 It accepts a regular function, and turns it into one that acts on monads like IO
. 它接受一个常规函数,并将其转换为一个像
IO
一样的monad。 It's actually a synonym for fmap
(in the standard Prelude), since all monads are functors. 它实际上是
fmap
的同义词(在标准Prelude中),因为所有monad都是fmap
函数。 So this code, roughly equivalent to @us202's, takes the result of fileRead
, inserts 5
, and gives you back the result wrapped in an IO
. 所以这个代码,大致相当于@ us202的,需要的结果
fileRead
,插入5
,并给你回包裹在一个结果IO
。
liftM (ins 5) fileRead
-- or --
fmap (ins 5) fileRead
I'd recommend the fmap
version. 我推荐
fmap
版本。 This code only makes use of the fact that IO
is a functor, so using liftM
implies to the reader that you might need it to be a monad too. 此代码仅使用
IO
是liftM
的事实,因此使用liftM
意味着读者可能还需要它作为monad。
'Lifting' is the general technique for using pure functions on values wrapped in monads or functors. “提升”是在包含在monad或functor中的值上使用纯函数的一般技术。 If you're unfamiliar with lifting (or if you're confused by monads and functors in general), I heartily recommend chapters 11-13 of Learn You A Haskell .
如果你不熟悉解除(或者如果你对monad和functor感到困惑),我衷心地推荐Learn You A Haskell的第11-13章。
PS. PS。 Note that the last two lines of
fileRead
should probably be combined, since return
doesn't really do anything: 请注意,
fileRead
的最后两行可能应该合并,因为return
实际上没有做任何事情:
fileRead :: (Read a, Ord a) => IO (Tree a)
fileRead = do file <- readFile "tree.txt"
return $ lstToTree $ map read $ words file
Or, since it's a short enough function, you could do away with do
notation altogether and use fmap
again: 或者,因为它是一个足够短的功能,你可以破除
do
完全符号和使用fmap
再次:
fileRead :: (Read a, Ord a) => IO (Tree a)
fileRead = fmap (lstToTree . map read . words) (readFile "tree.txt")
Edit in response to your comment: 编辑以回应您的评论:
Haskell is deliberately designed to keep code that performs IO separate from regular code. Haskell是故意设计让执行IO与普通的代码代码分离。 There's a very good philosophical reason for this: most Haskell functions are "pure" - that is, their output depends only on the input, just like functions in maths.
这有一个非常好的哲学原因:大多数Haskell函数都是“纯粹的” - 也就是说,它们的输出仅取决于输入,就像数学中的函数一样。 You can run a pure function a million times and you'll always get the same result.
你可以运行一百万次的纯函数,你总能得到相同的结果。 We like pure functions because they can't accidentally break other parts of your program, they permit laziness, and they allow the compiler to aggressively optimise your code for you.
我们喜欢纯函数,因为它们不会意外地破坏程序的其他部分,它们允许懒惰,并且它们允许编译器为您积极地优化代码。
Of course, in the real world we need a little bit of impurity. 当然,在现实世界中我们需要一点点杂质。 IO code like
getLine
can't possibly be pure (and a program that doesn't do IO is useless!). 像
getLine
这样的IO代码不可能是纯粹的(并且不执行IO的程序是无用的!)。 The result of getLine
depends on what the user typed: you could run getLine
a million times and get a different string every time. getLine
的结果取决于用户键入的内容:您可以运行getLine
一百万次并且每次都获得不同的字符串。 Haskell leverages the type system to label impure code with the type IO
. Haskell利用类型系统来标记
IO
类型的不纯代码。
Here's the crux of the matter: if you use a pure function on data obtained impurely then the result is still impure, because the outcome depends on what the user did . 问题的关键在于:如果对不纯粹获得的数据使用纯函数,那么结果仍然不纯,因为结果取决于用户的行为 。 So the whole calculation belongs in the
IO
monad. 所以整个计算都属于
IO
monad。 When you want to bring a pure function into IO
you have to lift it, either explicitly (using fmap
) or implicitly (with do
notation). 当你希望把一个纯粹的功能分为
IO
必须抬起它,明确地(使用fmap
)或隐式(带do
记号)。
This is a really common pattern in Haskell - look at my version of fileRead
above. 这是Haskell中一个非常常见的模式 - 看看我上面的
fileRead
版本。 I've used fmap
to operate on impure IO
data with a pure function. 我使用
fmap
来操作具有纯函数的不纯IO
数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.