readFile :: FilePath -> IO String
- file readwriteFile :: FilePath -> String -> IO ()
- write to filegetArgs :: IO [String]
- getting command line arguments (from the System.Environment
module)putStrLn :: String -> IO ()
- output a string, and carry over a string after it, to the consolemain :: IO () main = do src <- readFile "file.in" writeFile "file.out" (operate src) operate :: String -> String operate = ... is your function
main :: IO () main = do x1 <- expr1 x2 <- expr2 ... xN <- exprN return ()
do
keyword, then the sequence of instructions xI <- exprI
, and everything ends with return ()
. In each instruction to the left of the arrow is a sample (most often just a variable) of some type t
, and to the right is an expression of the type IO t
. Pattern-related variables can be used in subsequent instructions. If you want to use an expression whose type is different from IO t
, then you need to write xI <- return (exprI)
. The function return :: a -> IO a
takes any value and wraps it into type IO.main :: IO () main = do [arg1, arg2] <- getArgs src <- readFile arg1 res <- return (operate src) _ <- writeFile arg2 res return ()
operate
is still a pure function. In the first line after do
command line arguments are extracted using pattern matching. The second line reads the file whose name is specified in the first argument. The third line uses return
for a pure value of operate src
. The fourth line writes the result to a file. This does not give any useful result, so we ignore it by writing _ <-
._ <- x
you can simply write x
.<-
) on the last but one line and the expression is of type IO ()
, then the last line with return ()
can be deleted.x <- return y
can be replaced by let x = y
(if you do not use variable names again).main :: IO () main = do [arg1, arg2] <- getArgs src <- readFile arg1 let res = operate src writeFile arg2 res
main
function has the IO type, but we can create new functions of this type to avoid repeating the code. For example, we can write a helper function to print beautiful headings:title :: String -> IO () title str = do putStrLn str putStrLn (replicate (length str) '-') putStrLn ""
main
:main :: IO () main = do title "Hello" title "Goodbye"
return x
value of
, we write the return x
line in the last line of the do
block. Unlike the return
in imperative languages, this return
must be on the last line.readArgs :: IO (String, String) readArgs = do xs <- getArgs let x1 = if length xs> 0 then xs !! 0 else "file.in" let x2 = if length xs> 1 then xs !! 1 else "file.out" return (x1, x2)
main :: IO () main = do (arg1, arg2) <- readArgs src <- readFile arg1 let res = operate src writeFile arg2 res
if
we can choose which actions to perform. For example, if the user has not entered any arguments, we can report this:main :: IO () main = do xs <- getArgs if null xs then do putStrLn "You entered no arguments" else do putStrLn ("You entered" ++ show xs)
do
block is to do if
, and continue with do
in each of its branches. The only subtle point is that else
must indent at least one space more than if
. This is widely regarded as an error in the definition of Haskell, but for the moment, this additional space is indispensable.title
above, we can write:main :: IO () main = do let x = title "Welcome" x x x
<-
, we put the IO
value itself into the variable x
. x
is of type IO ()
, so now we can write
in a string to perform the action recorded in it. By writing x
three times, we perform this action three times.IO
values ​​as arguments to functions. In the previous example, we performed the action title "Welcome"
three times, but how could we execute it fifty times? We can write a function that takes an action and a number, and performs this action the appropriate number of times:replicateM_ :: Int -> IO () -> IO () replicateM_ n act = do if n == 0 then do return () else do act replicateM_ (n-1) act
main :: IO () main = do let x = title "Welcome" replicateM_ 3 x
for
statement in imperative languages ​​allows you to do the same thing as the replicateM_
function, but Haskell's flexibility allows you to define new control instructions — a very powerful tool. The replicateM_
function defined in Control.Monad is similar to ours, but more general; so you can use it instead of our version.IO
values ​​are passed as an argument, so it’s not surprising that we can put them in data structures, such as lists and tuples. The sequence_
function takes a list of actions and executes them in turn:sequence_ :: [IO ()] -> IO () sequence_ xs = do if null xs then do return () else do head xs sequence_ (tail xs)
sequence_
finishes work with return ()
. If there are any elements in the list, sequence_
selects the first action with head xs
and executes it, and then calls sequence_
on the rest of the tail xs
list. Like replicateM_
, sequence_
already present in Control.Monad in a more general form. Now you can easily rewrite replicateM_
using sequence_
:replicateM_ :: Int -> IO () -> IO () replicateM_ n act = sequence_ (replicate n act)
null/head/tail
. If there is exactly one instruction in the do
block, the word do
can be removed. For example, in the definition of sequence_
this can be done after the equal sign and after then
.sequence_ :: [IO ()] -> IO () sequence_ xs = if null xs then return () else do head xs sequence_ (tail xs)
if
with a mapping, as in any similar situation, without worrying about IO
:sequence_ :: [IO ()] -> IO () sequence_ [] = return () sequence_ (x: xs) = do x sequence_ xs
main :: IO () main = do xs <- getArgs sequence_ (map operateFile xs) operateFile :: FilePath -> IO () operateFile x = do src <- readFile x writeFile (x ++ ".out") (operate src) operate :: String -> String operate = ...
main
and operateFile
are part of the shell, and operate
and all the functions that it uses are clean. As a general design principle, try to make the action layer as thin as possible. The shell should briefly perform the necessary input, and the main work should be assigned to the clean part. Using explicit I / O in Haskell is necessary, but it should be kept to a minimum — pure Haskell is much prettier.Source: https://habr.com/ru/post/80396/
All Articles