IO基础 #
一、IO概念 #
1.1 什么是IO #
IO(Input/Output)是Haskell处理副作用的机制:
- 读取输入
- 写入输出
- 文件操作
- 网络通信
- 随机数
- 状态修改
1.2 IO类型 #
haskell
-- IO是一个类型构造器
-- IO a 表示一个产生类型a值的IO操作
-- 示例
getLine :: IO String -- 读取一行
putStrLn :: String -> IO () -- 输出一行
getChar :: IO Char -- 读取一个字符
1.3 纯函数与IO #
haskell
-- 纯函数:相同输入总是产生相同输出
add :: Int -> Int -> Int
add x y = x + y
-- IO操作:有副作用
main :: IO ()
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
二、基本IO操作 #
2.1 输出 #
haskell
-- putStrLn:输出字符串并换行
putStrLn :: String -> IO ()
-- putStr:输出字符串不换行
putStr :: String -> IO ()
-- putChar:输出单个字符
putChar :: Char -> IO ()
-- print:输出任意Show类型
print :: Show a => a -> IO ()
-- 示例
main :: IO ()
main = do
putStrLn "Hello, World!"
putStr "No newline"
putChar '!'
print 42
2.2 输入 #
haskell
-- getLine:读取一行
getLine :: IO String
-- getChar:读取一个字符
getChar :: IO Char
-- getContents:读取全部内容(惰性)
getContents :: IO String
-- 示例
main :: IO ()
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
2.3 交互 #
haskell
-- interact:将函数应用于整个输入
interact :: (String -> String) -> IO ()
-- 示例:将输入转为大写
main :: IO ()
main = interact (map toUpper)
-- 示例:只输出包含特定字符串的行
main :: IO ()
main = interact (unlines . filter (elem 'a') . lines)
三、IO组合 #
3.1 顺序执行 #
haskell
-- 使用>>顺序执行
(>>) :: IO a -> IO b -> IO b
-- 示例
main :: IO ()
main =
putStrLn "First" >>
putStrLn "Second" >>
putStrLn "Third"
3.2 绑定操作 #
haskell
-- 使用>>=绑定IO结果
(>>=) :: IO a -> (a -> IO b) -> IO b
-- 示例
main :: IO ()
main =
getLine >>= \name ->
putStrLn ("Hello, " ++ name)
3.3 return #
haskell
-- return:将值包装为IO
return :: a -> IO a
-- 示例
main :: IO ()
main = do
x <- return 5
print x -- 5
-- 注意:return不是返回语句
-- 它只是将值包装为IO
四、IO与纯函数 #
4.1 隔离副作用 #
haskell
-- 好:纯函数与IO分离
processName :: String -> String
processName name = "Hello, " ++ name ++ "!"
main :: IO ()
main = do
name <- getLine
putStrLn (processName name)
-- 不好:在纯函数中做IO
-- processName name = unsafePerformIO (putStrLn "debug")
4.2 从IO提取值 #
haskell
-- 不能直接从IO提取值
-- 错误示例:
-- name :: String
-- name = getLine -- 类型错误!
-- 正确方式:在IO上下文中使用
main :: IO ()
main = do
name <- getLine -- name :: String
putStrLn name
4.3 fmap和IO #
haskell
-- IO是Functor
-- 可以使用fmap处理IO结果
-- 示例
main :: IO ()
main = do
-- fmap处理IO结果
upperName <- fmap (map toUpper) getLine
putStrLn upperName
-- 等价于
name <- getLine
let upperName' = map toUpper name
putStrLn upperName'
五、IO工具函数 #
5.1 sequence #
haskell
import Control.Monad
-- sequence:将IO列表转为IO的列表
sequence :: [IO a] -> IO [a]
-- 示例
main :: IO ()
main = do
results <- sequence [getLine, getLine, getLine]
print results
-- sequence_:丢弃结果
sequence_ :: [IO a] -> IO ()
main' :: IO ()
main' = sequence_ [putStrLn "One", putStrLn "Two", putStrLn "Three"]
5.2 mapM #
haskell
import Control.Monad
-- mapM:映射后序列
mapM :: (a -> IO b) -> [a] -> IO [b]
-- 示例
main :: IO ()
main = do
results <- mapM getLine ["First: ", "Second: ", "Third: "]
print results
-- mapM_:丢弃结果
mapM_ :: (a -> IO b) -> [a] -> IO ()
main' :: IO ()
main' = mapM_ putStrLn ["One", "Two", "Three"]
5.3 forM #
haskell
import Control.Monad
-- forM:mapM的参数翻转
forM :: [a] -> (a -> IO b) -> IO [b]
-- 示例
main :: IO ()
main = do
results <- forM [1..3] $ \i -> do
putStrLn $ "Enter name " ++ show i ++ ":"
getLine
print results
5.4 when和unless #
haskell
import Control.Monad
-- when:条件为True时执行
when :: Bool -> IO () -> IO ()
-- unless:条件为False时执行
unless :: Bool -> IO () -> IO ()
-- 示例
main :: IO ()
main = do
putStrLn "Enter a number:"
n <- readLn
when (n > 0) $ putStrLn "Positive!"
when (n < 0) $ putStrLn "Negative!"
when (n == 0) $ putStrLn "Zero!"
六、实践示例 #
6.1 简单计算器 #
haskell
main :: IO ()
main = do
putStrLn "Simple Calculator"
putStrLn "Enter first number:"
a <- readLn :: IO Int
putStrLn "Enter second number:"
b <- readLn :: IO Int
putStrLn "Enter operation (+, -, *, /):"
op <- getLine
case op of
"+" -> print (a + b)
"-" -> print (a - b)
"*" -> print (a * b)
"/" -> print (a `div` b)
_ -> putStrLn "Unknown operation"
6.2 猜数字游戏 #
haskell
import System.Random
main :: IO ()
main = do
target <- randomRIO (1, 100) :: IO Int
putStrLn "I'm thinking of a number between 1 and 100"
gameLoop target
gameLoop :: Int -> IO ()
gameLoop target = do
putStrLn "Enter your guess:"
guess <- readLn :: IO Int
if guess < target
then do
putStrLn "Too low!"
gameLoop target
else if guess > target
then do
putStrLn "Too high!"
gameLoop target
else putStrLn "Correct! You win!"
6.3 文件复制 #
haskell
main :: IO ()
main = do
putStrLn "Enter source file:"
src <- getLine
putStrLn "Enter destination file:"
dst <- getLine
contents <- readFile src
writeFile dst contents
putStrLn "File copied successfully!"
七、总结 #
IO基础要点:
- IO类型:
IO a表示产生类型a值的IO操作 - 基本操作:
putStrLn、getLine、print - 组合操作:
>>、>>=、do语法 - return:将值包装为IO
- 工具函数:
sequence、mapM、forM、when - 隔离副作用:纯函数与IO分离
掌握IO基础后,让我们继续学习do语法。
最后更新:2026-03-27