对于纯函数语言来说,很难用一种清晰自然的方式来集成输入输出操作。对于一个IO库来说,基本上有四种操作:
考虑下这两种方向的操作的“函数”类型会是什么, 对于输出需要一个String 类型的参数,并且产生一个 unit () ,没有其他的返回。对于输入,返回一个 String ,没有参数。
这两种操作都不是 Function, 因为读入每一次的返回都是不确定的,不符合函数的定义。而对于输出,如果每次只是返回一个 () , 那么可以用任意一个函数 f _ = () 来替代这个输出函数。(基于函数的引用透明性)这显然不是我们想要的。
我们之所以会在输入输出碰到这样莫名其妙的问题,是因为我们在和“RealWorld” 打交道。如果我们把整个 RealWorld 当作一个 Type ,那么我们可以:
printAString :: RealWorld -> String -> RealWorld
readAString :: RealWorld -> (RealWorld, String)
把现实世界当作函数的一个参数。printAString 的输入是现实世界(当前的状态)和一个String,输出是一个新的现实世界。(因为有些东西被改变了,世界不同了)。
但是这个 真实世界 参数实在太讨厌了,很难以管理,一不小心就会搞乱世界。对于胸怀构建社会和谐秩序的程序员来说当然是不可接受的。
Phil Wadler 老大意识到 Monad是解决上述问题的王道!实际上, Monad被用来表达并发, 异常, IO, 非确定性计算 等等。Monad 可以在 haskell中被定义,而不需要借助于编译器(尽管在编译的时候经常会特殊优化他们)。
前文书讲过,打印和读取键盘一类的东西不算函数,所以换了个名字叫 Action,他们具有特别的类型。 putStrLn 就是一个特别有用的 Action:
putStrLn :: String -> IO ()
这个的意思是说, putStrLn 是一个 Action 带有一个 IO monad。
getLine :: IO String
getLine 是一个 IO Action ,执行时返回一个 String。
我们没有办法去亲自执行一个 Action,需要编译器来搞,一个可以执行的 haskell程序,必须有一个 main Action ,就像 C 中的 main 函数。 main :: IO()
我们通过 do 标记来组合不同的 Action:
main = do
putStrLn "Name:"
name <- getLine
putStrLn ("Hello " ++ name)
do : 顺序组合一系列的 Action
<- : 从一个 Action 中获得输出
读文件,写文件,读键盘,写屏幕,用过C的很容易理解,看例子吧
import Data.Time
import System.Locale
import System.Time
import System.IO
fact :: Int -> Int
fact n = if n == 0 then 1 else n * fact(n-1)
main = do
outfile <- openFile "f.txt" WriteMode
hSetBuffering stdin LineBuffering
putStrLn "Your name:"
name <- getLine
hPutStr outfile ("hello " ++ name ++ " ")
now <- getCurrentTime
hPutStrLn outfile $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M:%S" now
hPutStrLn outfile ("fact 5 = " ++ (show $ fact 5))
hClose outfile
在打开文件和关闭文件中间, 我们执行了很多操作,而这有可能会导致错误而无法正常关闭文件,为此,我们可以使用 bracket 函数:
bracket (openFile "somefile" ReadMode) hClose (\h -> hPutChar h c)
这个操作会出错,因为以 ReadMode打开,却执行了 hPutChar,这个时候第二个参数 hClose 将被执行。这种方式让我们不必太担心异常处理的事情。
看例子:
import System.IO
import IO
main :: IO ()
main = do
bracket
(openFile "f.txt" ReadMode)
(\h -> do
putStrLn "I catch the error, and will close the file"
hClose h)
(\h -> hPutStrLn h "hehe, this is wrong")
输出结果:
albert@albert-laptop:~/learn/unix$ runhaskell iowrong.hs I catch the error, and will close the file *** Exception: f.txt: hPutStr: illegal operation (handle is not open for writing)
书上的例子,比较有趣的是它的
case command of 'q':_ -> return () 'r':filename -> .... 'w':filename -> ... _ -> ....
这一部分的 Pattern Matching。
Recent comments
2 weeks 3 days ago
2 weeks 4 days ago
6 weeks 2 days ago
7 weeks 1 day ago
7 weeks 2 days ago
8 weeks 3 days ago
8 weeks 3 days ago
8 weeks 4 days ago
10 weeks 22 hours ago
10 weeks 3 days ago