
tl; dr :
A typical function that works withIOlooks almost like an imperative program:f :: IO a f = do x <- action1 action2 x y <- action3 action4 xy
- To assign a value to an object, we use
<-.- In this example, the type of expression in each line is
IO *;
action1 :: IO baction2 x :: IO ()action3 :: IO caction4 xy :: IO ax :: b,y :: c- Several objects have type
IO a.
With such objects, you cannot use pure functions.
In order to use pure functions, you will have to doaction2 (purefunction x).
Get a list of numbers from the user. Print their amount
toList :: String -> [Integer] toList input = read ("[" ++ input ++ "]") main = do putStrLn " ( ):" input <- getLine print $ sum (toList input) putStrLn :: String -> IO () getLine :: IO String print :: Show a => a -> IO () do block is of type IO a ? main = do putStrLn " ... " :: IO () getLine :: IO String print Something :: IO () <- . do x <- something something :: IO a , then x :: a .IO . All lines in the do-block should look like one of two ways: action1 :: IO a -- a = () value <- action2 -- -- bar zt :: IO b -- value :: b % runghc 02_progressive_io_example.lhs Enter a list of numbers (separated by comma): foo Prelude.read: no parse Maybe type. import Data.Maybe Maybe is a type that takes one parameter. Here is its definition: data Maybe a = Nothing | Just a maybeRead function is a great example of this approach.read function (which is very similar to the javascript eval function, which processes a JSON string.),Nothing .Just <> .read ; . maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing Nothing .Just [1,2,3] . getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" main :: IO () main = do putStrLn " , :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> print (sum l) Nothing -> error " . ." IO a .error .error msg function simply accepts any type of input ( IO () in our case).IO and this: main .getListFromString function. import Data.Maybe maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" askUser :: IO [Integer] askUser = do putStrLn " , :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser IO [Integer] .[Integer] using IO actions."This is[Integer]insideIO"
main :: IO () main = do list <- askUser print $ sum list IO . Everything went very quickly. Here are a few things that I want you to remember:do block, each expression must have type IO a .getLine , print , putStrLn , etc.IO a means the following - IO action , which returns a result of type a .IO type represents actions, and IO a is the type of the function.IO will not be a problem for you.Exercises :
- Write a program that summarizes all your arguments. Hint: use the
getArgsfunction.

tl; dr :
To distinguish pure functions,maindefined as a function that changes the state of the worldmain :: World -> World
Functions with this type are guaranteed to have side effects. Look at a typical main function:main w0 = let (v1,w1) = action1 w0 in let (v2,w2) = action2 v1 w1 in let (v3,w3) = action3 v2 w2 in action4 v3 w3
In this example, many temporary values ​​(w1,w2andw3)
which are used to transfer data to the next action.
We write thebindfunction or(>>=). Thanks tobindwe no longer need named temporary values.main = action1 >>= action2 >>= action3 >>= action4
Bonus: Haskell has syntax sugar for us:main = do v1 <- action1 v2 <- action2 v1 v3 <- action3 v2 action4 v3
IO ? For now, it all looks like some kind of magic. askUser :: IO [Integer] askUser = do putStrLn " , :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = do list <- askUser print $ sum list while analog.IO , the imperative style is more appropriate.main function can potentially change the state of the world. And its type will look something like this: main :: World -> World main function. main :: World -> ((),World) data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)} . All # are related to optimization, and in the example I swapped several fields. But in general, the idea has not changed.)() is an empty type. main w0 = let (list,w1) = askUser w0 in let (x,w2) = print (sum list,w1) in x World -> (a,World) a is the type of result.getChar function should be of type World -> (Char,World) .fab , you will have several options:a , then b then fabb , then a and finally fab .a and b and then fabprint works like this:((),__) . askUser :: World -> ([Integer],World) askUser :: IO [Integer] askUser = do putStrLn " :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser askUser w0 = let (_,w1) = putStrLn "Enter a list of numbers:" in let (input,w2) = getLine w1 in let (l,w3) = case getListFromString input of Just l -> (l,w2) Nothing -> askUser w2 in (l,w3) w* variables. let (y,w') = action xw in x not needed, the result will be a pair (answer, newWorldValue) . Each f function must have a type similar to: f :: World -> (a,World) let (y,w1) = action1 w0 in let (z,w2) = action2 w1 in let (t,w3) = action3 w2 in ... let (_,w1) = action1 x w0 in let (z,w2) = action2 w1 in let (_,w3) = action3 xz w2 in ... actionN w :: (World) -> (a,World) .IMPORTANT! There are only two patterns that are worth paying attention to:let (x,w1) = action1 w0 in let (y,w2) = action2 x w1 in
andlet (_,w1) = action1 w0 in let (y,w2) = action2 w1 in

bind two strings. To do this, write the function bind . bind :: (World -> (a,World)) -> (a -> (World -> (b,World))) -> (World -> (b,World)) (World -> (a,World)) is the type for IO action. type IO a = World -> (a, World) getLine :: IO String print :: Show a => a -> IO () getLine is an IO action that takes the world as a parameter and returns a pair (String,World) . You could say the type of getLine would be an IO String .print function is also quite interesting. It takes a parameter as input, which it displays later. But in fact, it takes two parameters. The first parameter is the value that will be displayed, the second is the state of the world. As a result, it returns a pair ((),World) . That is, it changes the state of the world, but does not return any data.bind function: bind :: IO a -> (a -> IO b) -> IO b bind takes 2 IO actions as arguments and returns another IO action. let (x,w1) = action1 w0 in let (y,w2) = action2 x w1 in (y,w2) action1 :: IO a action2 :: a -> IO b (y,w2) :: IO b (bind action1 action2) w0 = let (x, w1) = action1 w0 (y, w2) = action2 x w1 in (y, w2) let (line1,w1) = getLine w0 in let ((),w2) = print line1 in ((),w2) (res,w2) = (bind getLine (\l -> print l)) w0 (World -> ((),World)) , we know that res = () (null type). let (line1,w1) = getLine w0 in let (line2,w2) = getLine w1 in let ((),w3) = print (line1 ++ line2) in ((),w3) (res,w3) = bind getLine (\line1 -> bind getLine (\line2 -> print (line1 ++ line2))) bind with (>>=) .(>>=) is an infix function, the same as(+) ; remind 3 + 4 ⇔ (+) 3 4 (res,w3) = getLine >>= \line1 -> getLine >>= \line2 -> print (line1 ++ line2) do x <- action1 y <- action2 z <- action3 ... action1 >>= \x -> action2 >>= \y -> action3 >>= \z -> ... x in action2 and x with y in action3 .<- ?blindBind function: blindBind :: IO a -> IO b -> IO b blindBind action1 action2 w0 = bind action (\_ -> action2) w0 (>>) . do action1 action2 action3 action1 >> action2 >> action3 putInIO :: a -> IO a putInIO x = IO (\w -> (x,w)) putInIO is called return .return very different from analogues in other languages. askUser :: IO [Integer] askUser = do putStrLn " , :" input <- getLine let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = do list <- askUser print $ sum list import Data.Maybe maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of [(x,"")] -> Just x _ -> Nothing getListFromString :: String -> Maybe [Integer] getListFromString str = maybeRead $ "[" ++ str ++ "]" askUser :: IO [Integer] askUser = putStrLn " , :" >> getLine >>= \input -> let maybeList = getListFromString input in case maybeList of Just l -> return l Nothing -> askUser main :: IO () main = askUser >>= \list -> print $ sum list (>>) and (>>=) .
IO is a monad .do notations.Important note :
- Monads are not necessarily related to side effects!
There are many pure monads.- The essence of monads - in the composition of calculations
Monad is a type class.(>>=) and return .(>>) will be created automatically based on (>>=) .Monad class: class Monad m where (>>=) :: ma -> (a -> mb) -> mb return :: a -> ma (>>) :: ma -> mb -> mb f >> g = f >>= \_ -> g -- -- fail :: String -> ma fail = error Remarks:
- the
classkeyword is not what you think.
The class in Haskell is not a class from OOP.
A class in Haskell is more like an interface in Java or C #.
It would be better to call ittypeclass.
What does a lot of classes mean?
For a type to belong to this class, the type must implement all the functions of the class.- In this particular case, the type
mmust be a type that can take an argument.
for example,IO a, and alsoMaybe a,[a], etc.- To be a useful monad, your function must comply with some laws.
If your function violates these laws, strange things will happen:return a >>= k == ka m >>= return == m m >>= (\x -> kx >>= h) == (m >>= k) >>= h
Monad instances.Maybe .Maybe values, you can use monads to work with them. In particular, it can be useful to get rid of nested if..then..else.. deposit value account = account + value withdraw value account = account - value eligible :: (Num a,Ord a) => a -> Bool eligible account = let account1 = deposit 100 account in if (account1 < 0) then False else let account2 = withdraw 200 account1 in if (account2 < 0) then False else let account3 = deposit 100 account2 in if (account3 < 0) then False else let account4 = withdraw 300 account3 in if (account4 < 0) then False else let account5 = deposit 1000 account4 in if (account5 < 0) then False else True main = do print $ eligible 300 -- True print $ eligible 299 -- False deposit :: (Num a) => a -> a -> Maybe a deposit value account = Just (account + value) withdraw :: (Num a,Ord a) => a -> a -> Maybe a withdraw value account = if (account < value) then Nothing else Just (account - value) eligible :: (Num a, Ord a) => a -> Maybe Bool eligible account = do account1 <- deposit 100 account account2 <- withdraw 200 account1 account3 <- deposit 100 account2 account4 <- withdraw 300 account3 account5 <- deposit 1000 account4 Just True main = do print $ eligible 300 -- Just True print $ eligible 299 -- Nothing deposit :: (Num a) => a -> a -> Maybe a deposit value account = Just (account + value) withdraw :: (Num a,Ord a) => a -> a -> Maybe a withdraw value account = if (account < value) then Nothing else Just (account - value) eligible :: (Num a, Ord a) => a -> Maybe Bool eligible account = deposit 100 account >>= withdraw 200 >>= deposit 100 >>= withdraw 300 >>= deposit 1000 >> return True main = do print $ eligible 300 -- Just True print $ eligible 299 -- Nothing Maybe will work in most imperative languages.Important note:
The first calculation, the result of which isNothingwill stop all further calculations.
This means that not all code will be executed.
And this optimization is available to you absolutely free, thanks to the laziness of the language.
(>>=) for Maybe instance Monad Maybe where (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b Nothing >>= _ = Nothing (Just x) >>= f = fx return x = Just x Maybe monad has proven its usefulness even in such a small example. We also saw the use of the IO monad. But there is a more interesting example - lists.
import Control.Monad (guard) allCases = [1..10] resolve :: [(Int,Int,Int)] resolve = do x <- allCases y <- allCases z <- allCases guard $ 4*x + 2*y < z return (x,y,z) main = do print resolve [(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)] print $ [ (x,y,z) | x <- allCases, y <- allCases, z <- allCases, 4*x + 2*y < z ] shuffle = map (\x -> (x*3123) `mod` 4331) [1..] treeFromList treeFromList :: (Ord a) => [a] -> BinTree a treeFromList [] = Empty treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs)) (treeFromList (filter (>x) xs)) treeTakeDepth: treeTakeDepth _ Empty = Empty treeTakeDepth 0 _ = Empty treeTakeDepth n (Node x left right) = let nl = treeTakeDepth (n-1) left nr = treeTakeDepth (n-1) right in Node x nl nr main = do putStrLn "take 10 shuffle" print $ take 10 shuffle putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)" print $ treeTakeDepth 4 (treeFromList shuffle) % runghc 02_Hard_Part/41_Infinites_Structures.lhs take 10 shuffle [3123,1915,707,3830,2622,1414,206,3329,2121,913] treeTakeDepth 4 (treeFromList shuffle) < 3123 : |--1915 : | |--707 : | | |--206 : | | `--1414 : | `--2622 : | |--2121 : | `--2828 : `--3830 : |--3329 : | |--3240 : | `--3535 : `--4036 : |--3947 : `--4242 treeTakeDepth 4 (treeFromList [1..]) filter (<1) [2..].filternot smart enough to understand that the result of the expression will be an empty list.nthat treeTakeDepth n (treeFromList shuffle)will fall into an infinite loop.n.shufflelist, executing which, the program will end.treeFromListand shufflein order to get rid of this problem.shuffle.4331different numbers.shuffle. shuffle = map rand [1..] where rand x = ((px) `mod` (x+c)) - ((x+c) `div` 2) px = m*x^2 + n*x + o -- some polynome m = 3123 n = 31 o = 7641 c = 1237 filter (<x) xs.Any element of the left (or right) must be strictly less (or greater) than the value in the root of the tree.
treeFromListwe just replaced it filterwith safefilter. treeFromList :: (Ord a, Show a) => [a] -> BinTree a treeFromList [] = Empty treeFromList (x:xs) = Node x left right where left = treeFromList $ safefilter (<x) xs right = treeFromList $ safefilter (>x) xs safefilteralmost identical filterbut does not fall into an infinite loop when processing infinite trees. If she cannot find a suitable element in 10,000 steps, she interrupts the search. safefilter :: (a -> Bool) -> [a] -> [a] safefilter fl = safefilter' fl nbTry where nbTry = 10000 safefilter' _ _ 0 = [] safefilter' _ [] _ = [] safefilter' f (x:xs) n = if fx then x : safefilter' f xs nbTry else safefilter' f xs (n-1) main = do putStrLn "take 10 shuffle" print $ take 10 shuffle putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)" print $ treeTakeDepth 8 (treeFromList $ shuffle) 8to 100, thedeepand nbTry, the function works fine. But in the worst case, we can expect an exponential growth.treeFromList.[0,-1,-1,....,-1,1,-1,...,-1,1,...]).safefilteras follows: safefilter' fl = if filter f (take 10000 l) == []
then []
else filter fl
shuffle .safefilter' )f treeFromList' [] n = Empty treeFromList' (x:xs) n = Node x left right where left = treeFromList' (safefilter' (<x) xs (fn) right = treeFromList' (safefilter' (>x) xs (fn) f = ??? /r/haskellu/r/programming.Source: https://habr.com/ru/post/153383/
All Articles