series

  • Basic grammar for notes in the Haskell Guide to Fun Learning
  • Notes in the Haskell Guide to Fun Learning
  • Function of notes in the Haskell Guide to Fun Learning
  • Haskell’s Guide to Fun Learning notes of higher order functions
  • Module of notes in the Haskell Guide to Fun Learning
  • Custom types of notes in the Haskell Guide to Fun Learning
  • I/O notes from the Haskell Guide to Fun Learning

Hello World

  1. In helloWorld.hsmain = putStrLn "hello, world!
  2. runghc --make helloworld
  3. run./helloworld
  4. Get the outputhello, world!

Then let’s look at the type of the function

ghci> :t putStrLn 
putStrLn: :String -> IO(a)ghci> :t putStrLn "hello, world" 
putStrLn "hello, world": :IO(a)ghci> :k IO
IO: : * - > *ghci> :k IO(a)IO() : : *Copy the code

IO () returns an empty tuple of type (), also known as a unit. IO String, which appears in the next section, returns a String of type.

() Even a type is the value of the corresponding type.

Do grammar

The DO syntax allows you to combine multiple I/O operations into one.

main = do   
    putStrLn "Hello, what' s your name?"   
    name <- getLine &emsp; 
    putStrLn ("Hey " ++ name ++ ", you rock!") 
Copy the code
  • GetLine is of type IO String
  • GetLine is an I/O operation that produces a string
  • Pay attention toname <-It’s not writtenname =
  • The contents of an I/O operation can only be read in the context of an I/O operation, which is Haskell’s way of separating “pure code” from “impure code.
  • The DO syntax automatically takes the value of the last operation as its return value

The difference between IO String and String

nameTag = "Hello, my name is " ++ getLine 
Copy the code

++ The left-hand side is of type String and the right-hand side is of type IO String, so the above code will report an error. The String must be fetched by name < -getLine to continue. Impure data can only be processed in an impure environment. Otherwise impure code will contaminate the rest of the code like sewage, keeping I/ O-related code as small as possible is good for our health.

myLine = getLine

If I write the code like this

myLine = getLine 
Copy the code

This just adds an alias to getLine! The only way to get a value from an I/O operation is to use <-. Every time we press enter in GHCi, GHCi applies show to the return value and passes the resulting string to putStrLn for output to the terminal.

Return is different

Unlike other languages, Haskell returns can construct I/O operations based on a pure value. And return does not break the code. So, in the context of I/O, the return “hi” type is IO String. So what’s the point of converting a pure value to an I/ O operation that does nothing? One effect is that return () builds an I/O operation that does nothing.

Several I/O functions

  • putStr
  • putStr
  • print(equivalent to putstrln.show)
  • When <condition> $do IO operation
  • sequence [getLine getLine]<-Take the results of multiple I/O operations to form a list
  • mapM print [1, 2, 3]Is equivalent tosequence $ map print [1, 2, 3]
  • mapM_The return value version is not retainedmapM
  • Forever $do IO operation
  • ForM swaps the positions of mapM parameters, which is sometimes convenient
  • GetContents reads everything from standard input until it hits an end-of-file character, and getContents is lazy
  • Interact FN takes a function fn of type String -> String as an argument, returns an I/ O operation that takes input, applies a function to the input, and outputs the result of the function
  • GetArgs gets command line arguments
  • GetProgName Gets the program name

A read file stream is a set of data slices that enter and leave a program continuously over time.

  1. Create a file named test.txt with the following contents

     Hi! How are you?
     I'm Fine. Thank you. And you?
     I'm Fine too.
    Copy the code
  2. Create the file capslocker. Hs with the following contents

     import Control.Monad 
     import Data.Char 
     main = forever $ do
          l <- getLine
          putStrLn $ map toUpper l 
    Copy the code
  3. Compile: Run GHC –make Capslocker

  4. Pass the contents of test.txt to Capslocker: Run./ Capslocker < test.txt

  5. And then you’ll see that all the letters are capitalized and this code can be simplified with getContents

import Data.Char 
main = do
    contents <- getContents
    putStr $ map toUpper contents 
Copy the code

You can also run./ Capslocker without passing in the test.txt file and type lines of text, but note that eventually you need to press Ctrl+D to indicate the end of the content.

Stdin and stdout

One way to think about reading data from a terminal is to imagine that we’re reading a file. Output to a terminal can be understood similarly — it’s like writing a file. We can call these two files stdout and stdin for standard output and standard input, respectively.

Open the file with openFile

  1. Create gf. TXT as follows:

     Hey! Hey! You! You! 
     I don' t like your girlfriend! 
     No way! No way! 
     I think you need a new one! 
    Copy the code
  2. Create gF. hs as follows:

    import System.IO main = do handle <- openFile "gf. txt" ReadMode contents <- hGetContents handle putStr contents hClose Handle -- Note the openFile ReadMode hGetContents hClose functions, where the h prefix indicates that it receives handleCopy the code
  3. Compile and run How do I know what each parameter of openFile means?

Lambda > : t openFileopenFile: :FilePath -> IOMode -> IO HandleLambda > : the infoFilePath
type FilePath = String  -- Defined in 'ghc.io' λ> :info IOMode
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
        -- Defined in 'ghc.io.IOMode' λ> :info Handle
data Handle
  = GHC.IO.Handle.Types.FileHandle FilePath.Copy the code

Open the file with withFile

import System.IO 
main = do
     withFile "girlfriend.txt" ReadMode (\handle -> do
         contents <- hGetContents handle
         putStr contents) 
Copy the code

Bracket function

bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c 
Copy the code

How to use

bracket (openFile name mode)-- Open file
    (\handle -> hClose handle) -- What if I fail
    (\handle -> fn handle)         -- What if you succeed
Copy the code

WithFile is easy to implement using BRACKET.

Generating random numbers For a function, if it is called twice with the same arguments, it will return the same result twice. This is cool because it gives us a better understanding of the program, and it also allows us to delay the evaluation. However, this also makes generating random numbers difficult.

random :: (RandomGen g, Random a) => g -> (a, g) 
Copy the code

Random accepts a random number generator and returns a random number and a new random number generator. Then you can use the new generator to generate a new random number and a new generator. And so on. For the same generator, the resulting random number is fixed.

ghci>import System.Random
ghci> random (mkStdGen 100) : : (Int.StdGen) 
(- 1352021624..651872571 1655838864) 
ghci> random (mkStdGen 100) : : (Int.StdGen) 
(- 1352021624..651872571 1655838864)
ghci> random (mkStdGen 949494) : : (Int.StdGen) 
(539963926.466647808 1655838864)
ghci> random (mkStdGen 949488) : : (Float.StdGen) 
(0. 8938442.1597344447 1655838864) 
ghci> random (mkStdGen 949488) : : (Bool.StdGen) 
(False.1485632275 40692) 
ghci> random (mkStdGen 949488) : : (Integer.StdGen) 
(1691547873.1597344447 1655838864) 
Copy the code

Randoms takes a generator that returns an infinitely long list of random values

ghci> take 5 $ randoms (mkStdGen 11) : :Int] 
[- 1807975507..545074951, -1015194702, -1622477312, -502893664] 
Copy the code

No new generator is returned because the generator is at the end of the list, and the list is infinitely long…

RandomR generates a random number within a range

ghci> randomR (1.6) (mkStdGen 359353) 
(6.149428957840692) 
Copy the code

getStdGen

Before, we had to write a number every time we generated a random number, which was silly… So System.Random provides getStdGen, which asks the System for the initial global generator. But getStdGen is an IO operation that returns type IO stdGen.

import System.Random 
main = do
    gen <- getStdGen
    putStr $ take 20 (randomRs ('a',' z') gen) 
Copy the code

But if you call getStdGen twice, you’ll get the same gen. NewStdGen should be used the second time. In addition to returning a value of type IO stdGen, this function also updates the global generator. You call getStdGen and you get a different random number and then you wonder, why does getStdGen return a different result? Because it’s an I/O operation!

import System.Random

main = do
  gen <- getStdGen
  let a = fst ((random gen) :: (Int.StdGen))
  print a
  gen' <- newStdGen
  let b = fst ((random gen') :: (Int.StdGen))
  print b
  gen'' <- getStdGen
  let c = fst ((random gen'') :: (Int.StdGen))
  print c
  print "end"
Copy the code

This is code THAT I wrote blind.

Byte string bytestring

Lists like [1, 2, 3, 4] are just syntactic sugar for 1:2:3:4 :[]. When the first element is forced to be evaluated (say, to print it), the rest of the list 2:3:4 :[] is just a promise that will produce the promise of the list. We call this commitment a thunk. Thunk is basically a deferred calculation. There are two styles of byte strings: strict and lazy.

Strict ByteString disables inertia, no thunk. This is implemented in data.byteString. Lazy ByteStrings are lazy but more efficient than lists. This is implemented in data.byteString.lazy. Lazy bytes of data are stored in chunks (not to be confused with thunk), each 64 KB in size. So, if you evaluate a byte of an inert string (say, output it), the first 64 KB will be evaluated. After that, the remaining blocks are implemented as a promise (thunk). Use the sample

import qualified Data. ByteString. Lazy as B 
import qualified Data. ByteString as S

ghci> B. pack [99.97.110] 
Chunk "can" Empty 
ghci> B. pack [98.. 120] 
Chunk "bcdefghijklmnopqrstuvwx" Empty 
ghci> let by = B. pack [98.111.114.116] 
ghci> by 
Chunk "bort" Empty 
ghci> B. unpack by 
[98.111.114.116] 
ghci> B. fromChunks [S. pack [40.41.42].S. pack [43.44.45].S. pack [46.47.48]] 
Chunk "* ()" (Chunk "+, -" (Chunk "./0" Empty)) 
ghci> B. cons 85 $ B. pack [80.81.82.84] 
Chunk "U" (Chunk "PQRT" Empty) 
Copy the code