The point is, most times each of subsequent functions need to "see" what was in scope in the earlier functions. Most times, the later monadic actions need information that is pulled out of the monad in previous actions.
For example, consider the following:
main = putStrLn "Enter your first name" >>=
\_ -> getLine >>=
\first -> putStrLn "Enter your last name" >>=
\_ -> getLine >>=
\last -> putStrLn ("Hello " ++ first ++ " " ++ last ++ ".")
The last monadic action needs both "first" and "last" to both be in scope. This only works because of the way that it is implicitly parenthesized.
Until I "got" this, I was very confused by most of the Monad examples I found.
I wrote this up, "So You Want To Write A Monad Tutorial in Not-Haskell": http://www.jerf.org/iri/post/2928 It focuses on all the things I've seen people miss about "monads" when they try to write an implementation in another language after the third time I saw someone claim they'd implemented "monads" in Javascript using method chaining, which I do not believe to be possible in part because of the issue you point out, which is that the scope builds up as you go.
It is perfectly reasonable to also read this as the list of elements of monadery that even tutorials written in Haskell tend to miss or fail to explain.
What is your definition of "effects"? I ask because this article[0] and many others[1] seem[2] to use a contradictory definition, in which failure, nondeterminism, state etc. are viewed as effects and monads are a means of implementing those effects.
"Effect" is a more general idea. It's a lens for viewing any kind of impure computation[0]. Monads are a model for effects. You can view them as pure[1] computation in lambda calculus or as defining a region of code which, internally, has impure effects.
So to summarize: effects are a general concept, monads are a particular technology for implementing that concept.
Further complexity ensues when people start talking about the general concept of a monad which is interesting in its own right but it has a more sophisticated relationship with the concept of effects.
[0] Purity is a property of, say, functions. Its definition is a function `f` is pure if and only if
const () . f = const ()
which usually means that non-termination is impure as well. The notion of equality you use above can finesse this definition a lot.
[1] As stated in [0], non-termination is an effect, so Haskell monads are impure in that sense. Haskell typically ignores non-termination effects, though. Generally, monads would work more or less just fine even without non-termination though. Externally you can think of them as pure.
Ironically, I added that just as I was linking it here, despite the date on that post.
In addition to tel's discussion, what I was really trying to get at is that monads aren't "about" IO effects in particular, they're not about "impurity". In this case the whole thing is pure.
Defining effects at a deep programming language research level can bring a different understanding, where all monads are about effects, but "effects" has a different meaning that most people understand.
Your C# IO monad could really benefit from using LINQ (I know you mention it at the end). If you're interested in a full set of C# monads (including a fully implemented IO monad) check my csharp-monad library [0]
The example:
works for this example, but a more accurate representation of how "do" notation is expanded is: Note that I've moved the parentheses.Actually, you can, equivalently, omit the parentheses for the same meaning, because the '->' has very low precedence:
The point is, most times each of subsequent functions need to "see" what was in scope in the earlier functions. Most times, the later monadic actions need information that is pulled out of the monad in previous actions.For example, consider the following:
The last monadic action needs both "first" and "last" to both be in scope. This only works because of the way that it is implicitly parenthesized.Until I "got" this, I was very confused by most of the Monad examples I found.
Here's an article I wrote about the IO monad which also makes this point: https://github.com/Patient0/IOMonad