It means restricting to where you can perform IO actions, eg. printing to a log, writing to a shared area of memory, etc.
Haskell does this by representing IO actions as values, eg. "IO String" (something like "IO<String>", for people more familiar with that syntax), representing an IO action that, when later evaluated, will yield a string that you can use in subsequent actions.
Haskell's "main" (entry-point) function is a value of type "IO ()" (IO of a value representing nothing useful; you don't get anything out of it, so you just have a nothing placeholder). When a Haskell program is run, it ostensibly (hand-waving here) takes that IO value and interprets it, performing the action IO.
You have functions like putStrLn (print a line) that takes a String, and returns an "IO ()" . Accordingly, if you have
main = putStrLn "Hello, World!"
... you have a value that lines up with the expected return value of main "IO ()". More complicated examples, like
... are doing the same thing (checking the types line up), just with more moving parts (the Monad thing you mentioned, which we're using to relate the "read line" function with the "print line" function).
The key bit is that it's just "IO Blah" values standing in for IO actions, and like jigsaw pieces, these IO-returning functions only fit together where the types line up, restricting how you can use them.
Monads are tangential to this; the type-checking of that jigsaw puzzle is what matters here.
Haskell does this by representing IO actions as values, eg. "IO String" (something like "IO<String>", for people more familiar with that syntax), representing an IO action that, when later evaluated, will yield a string that you can use in subsequent actions.
Haskell's "main" (entry-point) function is a value of type "IO ()" (IO of a value representing nothing useful; you don't get anything out of it, so you just have a nothing placeholder). When a Haskell program is run, it ostensibly (hand-waving here) takes that IO value and interprets it, performing the action IO.
You have functions like putStrLn (print a line) that takes a String, and returns an "IO ()" . Accordingly, if you have
... you have a value that lines up with the expected return value of main "IO ()". More complicated examples, like ... are doing the same thing (checking the types line up), just with more moving parts (the Monad thing you mentioned, which we're using to relate the "read line" function with the "print line" function).The key bit is that it's just "IO Blah" values standing in for IO actions, and like jigsaw pieces, these IO-returning functions only fit together where the types line up, restricting how you can use them.
Monads are tangential to this; the type-checking of that jigsaw puzzle is what matters here.