Newbie: understanding main and IO ()

During Haskell training, I wonder when the IO action will be executed. In several places I found such descriptions:

"The peculiarity of I / O operations is that if they fall into the main function, they are executed."

But in the following example, "greet" never returns, and therefore nothing needs to be printed.

import Control.Monad main = greet greet = forever $ putStrLn "Hello World!" 

Or maybe I should ask: what does "get into the main function" mean?

+2
source share
5 answers

First of all, main not a function. This is really just a regular value, and its type is IO () . A type can be read as: An action that, when executed, produces a value of type () .

Now the runtime system acts as an interpreter that performs the actions described by you. Take your program as an example:

 main = forever (putStrLn "Hello world!") 

Please note that I performed the conversion. This is true because Haskell is a referentially transparent language. The runtime system resolves the forever and finds this:

 main = putStrLn "Hello world!" >> MORE1 

He does not yet know what MORE1 , but now he knows that he has a composition with one known action that is performed. After its execution, it resolves the second action, MORE1 and finds:

 MORE1 = putStrLn "Hello world!" >> MORE2 

Again he performs the first action in this composition, and then continues to resolve.

Of course, this is a high-level description. Actual code is not an interpreter. But this is a way to show how the Haskell program is executed. Take another example:

 main = forever (getLine >>= putStrLn) 

RTS sees this:

 main = forever MORE1 << resolving forever >> MORE1 = getLine >>= MORE2 << executing getLine >> MORE2 result = putStrLn result >> MORE1 << executing putStrLn result (where 'result' is the line read) and starting over >> 

Understanding this, you understand how an IO String not a “side effect string”, but rather a description of the action that would create the string. You also understand why laziness is critical to the operation of the Haskell I / O system.

+10
source

In my opinion, the point of approval is “What is especially important for I / O operations is that if they fall into the main function, they are executed.” lies in the fact that IO actions are first class citizens. That is, IO actions can occur in all places where values ​​of other data types, such as Int . For example, you can define a list containing IO actions as follows.

 actionList = [putStr "Hello", putStr "World"] 

The actionList is of type [IO ()] . That is, the list contains actions that interact with the world, for example, printing on the console or reading input from the user. But when defining this list, we do not perform actions, we simply put them on the list for future use.

If IO can happen somewhere in your program, the question arises when these actions are performed, and here main enters the game. Consider the following definition of main .

 main = do actionList !! 0 actionList !! 1 

This main function implements the first and second components of the list and performs the corresponding actions using them in its definition. Note that there does not have to be a main function that performs an IO action. Any function called from the main function can also perform actions. For example, we can define a function that calls actions from an actionList and main call this function as follows.

 main = do caller putStr "!" caller = do actionList !! 0 actionList !! 1 

To emphasize that this should not be a simple renaming, for example, in main = caller , I added an action that prints an exclamation point after it performs the actions from the list.

+5
source

Simple I / O actions can be combined into more complex I / O using notation.

 main = do printStrLn "Hello" printStrLn "World" 

combines the IO printStrLn "Hello" action with the IO printStrLn "World" action. Currently, Main is an I / O initial, first printing a line that says "Hi," and then a line that says "World." Written without do-notation (which is just syntactic suger) looks like this:

 main = printStrLn "Hello" >> printStrLn "World" 

Here you can see the >> function combining the two actions.

You can create an I / O operation that reads a string, passes it to a function (which makes amazing stuff for it :)), and prints the result as follows:

 main = do input <- getLine let result = doAwesomeStuff input printStrLn result 

or without binding the result to a variable:

 main = do input <- getLine printStrLn (doAwesomeStuff input) 

It can also be written as IO actions and functions that combine them as follows:

 main = getLine >>= (\input -> printStrLn (doAwesomeStuff input)) 

When the program starts, the main I / O action is performed. This is the only case where IO actions are actually performed. (Well, technically, you can also execute them inside your program, but this is unsafe. The function that does this is called unsafePerformIO .)

You can read more here: http://www.haskell.org/haskellwiki/Introduction_to_Haskell_IO/Actions

(This link is probably a better explanation than mine, but I found it only after I wrote almost everything. It's a little longer)

+2
source
 launchAMissile :: IO () launchAMissile = do openASilo loadCoordinates launchAMissile main = do let launch3missiles = launchAMissile >> launchAMissile >> launchAMissile putStrLn "Not actually launching any missiles" 
+1
source

forever not a loop like C while (true) . This is a function that produces an IO value (which contains an infinitely repeating sequence of actions) that is consumed by the caller. (In this case, the calling object is main , which means that the actions are performed by the runtime system).

+1
source

Source: https://habr.com/ru/post/1265149/


All Articles