# Dialogues vs continuations (and algebraic effects) to implement I/O

**URL:** <https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857>\
**Category:** Uncategorized\
**Created:** [November 30, 2024, 10:47am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857 "2024-11-30T10:47:31Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [November 30, 2024, 10:47am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/1 "2024-11-30T10:47:31Z")

</div>

> [@Why use an effect system?](http://discourse.haskell.org/t/why-use-an-effect-system/10841/19):
>
> However, there is another option for those who prefer a more denotative approach to effects, at least for I/O:
> 
> [dialogue: I/O in Haskell Report 1.2](https://hackage.haskell.org/package/dialogue)

Here’s how you can implement the same thing using algebraic effects:

> **[GitHub - noughtmare/free-io: Pure model of IO using the free monad](https://github.com/noughtmare/free-io)**
>
> Pure model of IO using the free monad

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 10:56am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/2 "2024-11-30T10:56:16Z")

</div>

…and from 2008:

[https://web.archive.org/web/20090215004126/http://lukepalmer.wordpress.com/2008/03/29/io-monad-the-continuation-presentation](https://web.archive.org/web/20090215004126/http://lukepalmer.wordpress.com/2008/03/29/io-monad-the-continuation-presentation)

So are there any substantial differences?

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [November 30, 2024, 11:15am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/3 "2024-11-30T11:15:26Z")

</div>

Indeed, even in 2008 people already knew about this. See also the famous [Data Types a la Carte](https://www.cambridge.org/core/journals/journal-of-functional-programming/article/data-types-a-la-carte/14416CB20C4637164EA9F77097909409) paper. I just mean to say that the dialogue approach is outdated. There is a better way to model I/O now, namely using algebraic effects! I see no reason to keep linking the dialogue approach except as a historical curiosity.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 11:55am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/4 "2024-11-30T11:55:36Z")

</div>

> Indeed, even in 2008 people already knew about this.

`o_0` …algebraic effects _using continuations_? Those have been around since (at least!) the early 1990s; for example, Nigel Perry’s _result continuations_ in his Ph.D thesis. But Haskell continued to use its dialogue-based continuations until 1996.

So if algebraic effects are so amazing…why didn’t they replace dialogues back then?

* * *

> I see no reason to keep linking the dialogue approach except as a historical curiosity.

…and for a time it was standard Haskell. So what are the chances of any effect system _ever_ being part of a future Haskell standard?

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [November 30, 2024, 6:39pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/5 "2024-11-30T18:39:31Z")

</div>

> [@atravers](#):
>
> `o_0` …algebraic effects _using continuations_? Those have been around since (at least!) the early 1990s; for example, Nigel Perry’s _result continuations_ in his Ph.D thesis. But Haskell continued to use its dialogue-based continuations until 1996.
> 
> So if algebraic effects are so amazing…why didn’t they replace dialogues back then?

Phil Wadler beat Nigel Perry to the punch with his [Comprehending Monads](https://dl.acm.org/doi/pdf/10.1145/91556.91592) paper in 1990, and showed together with @simonpj in [1992 that you could also use them for I/O](https://dl.acm.org/doi/pdf/10.1145/158511.158524). I don’t know why it took another 4 more years to work out the details, but I’d imagine people back then were already convinced monads would be the way forward for Haskell’s I/O.

I personally think it is quite a shame that representing effectful programs using continuations was so quickly discarded, because I think continuations (a.k.a. callbacks) are much easier to explain to beginners than the current `State# RealWorld` business. I imagine performance was the biggest consideration; using a lambda for every continuation is not free. Though, I think it would not be impossible to optimize the overhead away. We now have join points which can do this locally within a function, so we’d only need some kind of annotation to propagate this information across function boundaries (such annotations could even be reserved for these built-in I/O operations, so users never even have to think about them).

However, just representing a program as one big pure algebraic data type does not buy you much. If you cut your program up into many small pure continuation “slices” which are separated by opaque I/O operations, many properties which you might want to prove will inevitably cross over an opaque I/O operation which prevents further reasoning. That is, unless you have laws which tell you how those I/O operations behave, that is exactly what algebraic operations ([Plotkin and Power, 2003](https://homepages.inf.ed.ac.uk/gdp/publications/alg_ops_gen_effects.pdf)) add to the continuation approach.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 7:38pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/6 "2024-11-30T19:38:51Z")

</div>

> I think continuations (a.k.a. callbacks) are much easier to explain than the current `State# RealWorld` business.

- [Continuation tutorials timeline - HaskellWiki](https://wiki.haskell.org/Continuation_tutorials_timeline)

- [Understanding Callbacks and Callback Hell in JavaScript - GeeksforGeeks](https://www.geeksforgeeks.org/what-to-understand-callback-and-callback-hell-in-javascript)

* * *

> I imagine performance was the biggest consideration; using a lambda for every continuation is not free.

There’s another problem - reuse of exposed continuations:

```haskell
(\ k -> k 1 + k 2)

```

…much like reuse of exposed `State# RealWorld` values, both of which are solved by using an abstract data type…which then leaves the choice of interface that abstract type should use:

> **[io-machine](https://hackage.haskell.org/package/io-machine)**
>
> Easy I/O model to learn IO monad

* * *

> […] you have laws which tell you how those I/O operations behave […]

So would this entail:

- adding new laws for each FFI I/O call?
- then checking what other I/O laws are affected?

That looks rather like _O_(_n_ 2) complexity to me - alright for a few primitive or FFI I/O operations; not so wonderful if you’re using a lot of them. And remember - [side effects are arbitrary](https://youtu.be/lRU6TDgadqw?t=372), so there can be an arbitrary number of them…

* * *

> _Plotkin and Power_, 2003

> [@Why use an effect system?](http://discourse.haskell.org/t/why-use-an-effect-system/10841/48):
>
> New Haskellers are having enough problems with the monadic interface: […] …do they really need more problems by _“giving”_ them [extra artefacts](https://www.math.columbia.edu/~esaunders/YonedaLemma.pdf) from [one of the most abstract branches of mathematics](https://www.mathematik.uni-marburg.de/~loogen/Lehre/ws06/Konzepte/tackling.pdf)?

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [November 30, 2024, 7:55pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/7 "2024-11-30T19:55:30Z")

</div>

> [@atravers](#):
>
> There’s another problem - reuse of exposed continuations:
> 
> ```haskell
> (\ k -> k 1 + k 2)
> 
> ```

That’s not how the continuations in these systems work. The user never gets their hands on a continuation. Instead, the user only provides a continuation to these primitive I/O operations.

The operations are implemented either as built-in functions in the language, or as data constructors. I’d prefer the latter for transparency reasons, but the former might be more efficient.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 8:04pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/8 "2024-11-30T20:04:12Z")

</div>

> That’s not how the continuations in these systems work. The user never gets their hands on a continuation.

I presume you’re referring to this paragraph from [Imperative Functional Programming](https://dl.acm.org/doi/pdf/10.1145/158511.158524):

> [@](#):
>
> (page 5 of 14)
> 
> An obvious improvement, which we have not seen previously suggested, is to implement the primitive continuation operations (such as `putcC`, `getcC` and `doneC`) directly, making the `Result` type an abstract data type with no operations defined on it other than the primitives themselves. This solves the problem. Fur

Well, this is more-or-less what the monadic interface supports:

```haskell
unitIO :: a -> IO a
bindIO :: IO a -> (a -> IO b) -> IO b

```

In both cases, the user provides a continuation that accepts _the value produced by an I/O action_, rather than a rest-of-the-program continuation. So this would seem to be _yet_ another reason in favour of having an abstract monadic I/O type…

`(`…hmm; these different varieties of continuations could be confusing for those new to FP - anyone for a tutorial? `;-)`

* * *

> Instead, the user only provides a continuation to these primitive I/O operations.

This can still cause problems:

> [@](#):
>
> (page 20 of 33)
> 
> […] with continuations, it is easy to write a version of `echo` that composes, if you remember to include a continuation argument and with monads, it is _impossible_ to write a version of `echo` that does _not_ compose.
> 
> [How to Declare an Imperative](https://ics.uci.edu/~jajones/INF102-S18/readings/24_wadler) (1997)

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [November 30, 2024, 8:20pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/9 "2024-11-30T20:20:31Z")

</div>

That problem can be mitigated by providing a monadic interface on top of the continuation-based I/O primitives, like I did in that `free-io` example:

> <https://github.com/noughtmare/free-io/blob/b93db6c2a8db5c36db4d913d81729f6476e059a0/src/System/IO/Free.hs#L53>

The benefit of using continuations under the hood would be that we can easily show to beginners simple examples, like how `getChar >>= putChar` evaluates to something like `\k -> GetChar (\c -> PutChar c k)`, which only uses an ADT and lambdas. I know those two things are also something people need to learn, but I’d expect it to be much easier to grasp than to just say people should not look at the internals of `IO`, or worse, try to explain whatever `State# RealWorld -> (# Char, State# RealWorld #)` means.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 10:06pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/10 "2024-11-30T22:06:55Z")

</div>

> […] we can easily show to beginners simple examples […]

- 

> [@](#):
>
> (page 60 of 159)
> 
> **2.7.3 Effort**
> 
> Some of the programming required to achieve the most basic interaction is quite detailed. More so, a lot of it is fluff, dealing with verbosity of the I/O system, or awkward error handling. Often it is hard to see the useful code amongst all the bits of irrelevant code holding it all together. I want a more direct style of coding. Notice that the monadic style has its own limitations, and it is not a complete solution to interfacing functional languages to external systems.
> 
> [Interacting with Functional Languages](https://core.ac.uk/download/pdf/293056217.pdf) (1997)

- 

> [@](#):
>
> In the pure functional languages, Haskell and Clean, at present the most developed ones, the I/O problem is solved by respectively monads and uniqueness typing. But using these features, in both cases it is still possible to write incomprehensible code when dealing with I/O.
> 
> [Gems of Corrado Böhm](https://lmcs.episciences.org/6755/pdf) (2020).

- 

> [@](#):
>
> This is hard stuff. Two years ago I spent several hours to write 3 lines invoking IO computations.
> 
> [Trying to understand the IO () - #8 by belka](http://discourse.haskell.org/t/trying-to-understand-the-io/1172/8) (2020)

- 

> [@](#):
>
> […] it is really quite annoying having to turn a nice elegant pure traversal of your AST into a monadic [I/O] beast […]
> 
> [Solving cyclic boolean implications with pure code and laziness](http://discourse.haskell.org/t/solving-cyclic-boolean-implications-with-pure-code-and-laziness/4951) (2022)

…and they’re _experienced_ users of functional programming! There’s also this:

- 

> [@](#):
>
> (pages 36-37 of 42)
> 
> **Booch** : What would be your advice to somebody that would want to take up the banner of functional programming? Where would you suggest they begin, and what hard problems would you like them to pursue?
> 
> **Backus** : Well, trying to functionalize all the input/output stuff.
> 
> **Booch** : Helping it talk to the real world.
> 
> **Backus** : Yes.
> 
> [Oral History of John Backus](https://archive.computerhistory.org/resources/text/Oral_History/Backus_John/Backus_John_1.oral_history.2006.102657970.pdf), (2006)

…yes, [**_that_**](https://www.cs.cmu.edu/~crary/819-f09/Backus78.pdf) John Backus!

Alright, that’s the experienced users - what about potential new users? Here’s some observations about programmers in other languages who are new to Haskell:

- 

> [@](#):
>
> Since I/O is by nature side effects, Haskell handles it differently. Monadic I/O is used to overcome this problem.
> 
> After overcoming Haskell’s I/O, there were no more setbacks in writing the program.
> 
> [Functional Programming Using Haskell](https://www.mta.ca/~rrosebru/oldcourse/371199/haskell/paper.htm) (1999)

- 

> [@](#):
>
> As for intrinsic advantage, I think Haskell has at least one obvious handicap – monadic I/O. Every minute I spend trying to understand how to program with it is a minute I am solving a problem I don’t care about.
> 
> [New Paul Graham thing (_comment_ )](http://lambda-the-ultimate.org/node/186#comment-1436) (2004)

- 

> [@](#):
>
> IO in Haskell [is] for me a source of great displeasure and it just defeated every try I have given to learn the language.
> 
> [Introduction to Haskell IO (_comment_ )](https://www.haskellforall.com/2013/01/introduction-to-haskell-io.html?showComment=1361296801442#c794624017122358881) (2013)

- 

> [@](#):
>
> Understanding I/O in Haskell, which implies understanding Monads (at least, the `IO` Monad) is actually one of the major difficulties I’ve came across while learning Haskell […]
> 
> [Is it possible to solve TSORT with Haskell? (_comment_ )](https://discuss.codechef.com/t/is-it-possible-to-solve-tsort-with-haskell/4450/2) (2014)

- 

> [@](#):
>
> […] I keep on meeting random programmers, and they all know Haskell, or at least some Haskell. Right now, there’s quite a bunch of Haskvangelists who are targeting existing programming communities, and they get diminishing returns because their target audience either knows some Haskell or does not have the time and patience to learn and apply Haskell properly.
> 
> [Looking for Haskell / Comp Sci Tutor](http://discourse.haskell.org/t/looking-for-haskell-comp-sci-tutor/4195) (2022)

As for _“completely-new”_ programmers to Haskell:

- 

> [@](#):
>
> In the first iteration, I/O was covered toward the end of the course because it is connected with the advanced topic of monads. […] we [subsequently] moved I/O to an earlier point in the course. We also dropped monads, since the majority [of students] had not grasped them.
> 
> [Experience Report: The Next 1100 Haskell Programmers](https://www21.in.tum.de/~blanchet/teaching_haskell2.pdf) (2014)

- 

> [@](#):
>
> In order to keep the programming as simple as possible, we chose to use only a subset of Haskell without monads, higher-level categorical interfaces or IO.
> 
> [Haskell in Middle and High School Mathematics](https://wiki.tfpie.science.ru.nl/images/3/32/Alegre.pdf) (2015)

- 

> [@](#):
>
> The [decision to split off I/O from monads and introduce it earlier] is done in an effort to convince students more quickly that pure functional languages can be practical and deal with side effects.
> 
> [Engaging, Large-Scale Functional Programming Education in Physical and Virtual Space](https://arxiv.org/pdf/2207.12703.pdf) (2022)

- 

> [@](#):
>
> The most difficult construct for students to understand is the monad. I introduce `IO` without mentioning monads. […] However, I fear that just to understand the bind operator `>>=` requires people to be quite comfortable with higher-order functions.
> 
> [Haskell in education - HaskellWiki](https://wiki.haskell.org/Haskell_in_education) : _Functional Programming in Haskell_, RWTH Aachen ([2006](https://wiki.haskell.org/index.php?title=Haskell_in_education&oldid=2005))

Beginners don’t need more simple examples - they need a simple model of I/O in Haskell, one that doesn’t leave them feeling some thing like:

[![](https://i.ytimg.com/vi/XUdaD1zUXDM/hqdefault.jpg "stickman VS door full version") ](https://www.youtube.com/watch?v=XUdaD1zUXDM)

…and end up being more _“random programmers who knows Haskell, or at least some Haskell”_.

* * *

> I know those two things are also something people need to learn […]

Only for the likes of Haskell, Agda and Idris:

- 

> [@](#):
>
> (page 46 of 72)
> 
> Just as with the [SAC] language kernel, programmers with a background in imperative programming should not be bothered by the conceptual troubles of manipulating the state of devices in a state-free environment. We certainly do not want our programmers to familiarise themselves with theoretically demanding concepts such as monads and uniqueness types. And we definitely do not want to rely on our programmers being experts in category theory to write a _hello world_ program in SAC.
> 
> [Single Assignment C – High Productivity Meets High Performance](https://www.sac-home.org/_media/publications:pdf:2012_1.pdf) (2012)

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [November 30, 2024, 10:55pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/11 "2024-11-30T22:55:20Z")

</div>

> [@atravers](#):
>
> Beginners don’t need more simple examples - they need a simple model of I/O in Haskell

I don’t understand why beginners need a “simple model of I/O in Haskell”. Do Python beginners need a “simple model of I/O in Python”?

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [November 30, 2024, 11:32pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/12 "2024-11-30T23:32:53Z")

</div>

> I don’t understand why beginners need a “simple model of I/O in Haskell”. Do Python beginners need a “simple model of I/O in Python”?

(…nice attitude - the beginners will be impressed!)

I believe this is from some educators who built their own object-oriented programming language specifically for teaching purposes:

> [@](#):
>
> […] in most programming languages input and output are esoteric and the techniques for performing input and output must be learnt by the students at an early stage, precisely when they are trying to understand the basics of programming.
> 
> [I/O Considered Harmful (At Least for the First Few Weeks)](https://www.cs.kent.ac.uk/pubs/1997/2176/content.pdf)

Moreover:

> [@](#):
>
> By its very nature I/O does not fit in well with programming language design. Every language designer has encountered the problem that the task of including usable I/O facilities invariably seems to make it necessary to break some of the rules or design principles of the language. In many languages with clean and simple concepts I/O is the “odd one out”.

…and for GHC:

* * *

- 

> [@](#):
>
> ```haskell
> data State# a :: ZeroBitType
> 
> ```
> 
> `State#` is the primitive, unlifted type of states. It has one type parameter, thus `State# RealWorld`, or `State# s`, where `s` is a type variable. The only purpose of the type parameter is to keep different state threads separate. It is represented by nothing at all.
> 
> [GHC.Prim](https://hackage.haskell.org/package/ghc-prim-0.11.0/docs/GHC-Prim.html#t:State-35-) (`State#`)

- 

> [@](#):
>
> ```haskell
> type ZeroBitType = TYPE ZeroBitRep
> 
> ```
> 
> The kind of the empty unboxed tuple type `(# #)`
> 
> [GHC.Types](https://hackage.haskell.org/package/ghc-prim-0.11.0/docs/src/GHC.Types.html#ZeroBitType) (`ZeroBitType`)

- 

> [@](#):
>
> ```haskell
> type Void# = (# #)
> 
> ```
> 
> Deprecated: `Void#` is now an alias for the unboxed tuple `(# #)`
> 
> [GHC.Types](https://hackage.haskell.org/package/ghc-prim-0.11.0/docs/GHC-Types.html#t:Void-35-) (`Void#`)

* * *

…to paraphrase a line from a sci-fi classic:

### There is no `State# RealWorld`.

### 

Hence **jaror** ’s lament about trying to explain whatever `State# RealWorld -> (# Char, State# RealWorld #)` means, _to beginners_.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [December 1, 2024, 8:15am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/13 "2024-12-01T08:15:46Z")

</div>

> [@atravers](#):
>
> Hence **jaror** ’s lament about trying to explain whatever `State# RealWorld -> (# Char, State# RealWorld #)` means, _to beginners_.

But I still don’t understand why one should try to explain that to beginners. `IO`, as implemented in a Haskell compiler, could well be a completely abstract type, in the sense that the compiler doesn’t even _try_ to give it an implementation in Haskell, like GHC does with `State# RealWorld`.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [December 1, 2024, 9:57am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/14 "2024-12-01T09:57:27Z")

</div>

I think many programmers learn things by looking at their definitions. When looking at I/O some will inevitably end up here:

[https://hackage.haskell.org/package/base-4.20.0.1/docs/Prelude.html#t:IO](https://hackage.haskell.org/package/base-4.20.0.1/docs/Prelude.html#t:IO)

And then click “source” which takes them here:

[https://hackage.haskell.org/package/ghc-prim-0.11.0/docs/src/GHC.Types.html#IO](https://hackage.haskell.org/package/ghc-prim-0.11.0/docs/src/GHC.Types.html#IO)

And leave much more confused then when they started.

* * *

More importantly, I think this “tree of operations with continuations” model could be a good on-ramp to explaining I/O without having to mention monads. For example, inspired by the [Hedy lessons](https://hedy.org/hedy#ask_command), you could start by allowing only printing lines like this:

```haskell
data Action = Print String

lesson0 = [Print "Hello, World!"]

lesson1 = [ Print "Hi there, programmer!"
          , Print "Welcome to Haskell!"
          ]

```

Then you could introduce `ask` and `echo`.

```haskell
data Action = Print String | Ask String | Echo String

lesson2 = [ Print "Hello!"
          , Ask "What is your name?"
          , Echo "hello " -- the answer will be appended here
          ]

```

Of course just echoing the last answer is very limited. We might want to refer to these answers multiple times or only later on in the program. A list is no longer sufficient. Instead we can use continuations:

```haskell
data Action = Print String Action | Ask String (String -> Action) | Halt

lesson3 = 
  Ask "What is your name?" $ \name ->
  Print ("Hello " ++ name) $
  Ask "How old are you?" $ \age ->
  Print (name ++ " is " ++ age ++ "years old.") $
  Halt

```

Etc.

---

<div class="post-metadata">

**Author:** ![Kleidukos](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kleidukos/32/1213_2.png) [@Kleidukos](https://discourse.haskell.org/u/Kleidukos)\
**Post date:** [December 1, 2024, 10:39am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/15 "2024-12-01T10:39:03Z")

</div>

I absolutely agree with you. Never have I ever showed any beginner the definition of IO. It’s useless and defeats the point of teaching to beginners.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [December 1, 2024, 4:20pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/16 "2024-12-01T16:20:20Z")

</div>

> Never have I ever showed any beginner the definition of `IO a`.

You don’t have to:

> [@](#):
>
> I remember when I first got my head around IO, the type of  
> `(>>=) :: IO a -> (a -> IO b) -> IO b`  
> confused me something fierce: If I “can’t get the `a` out”, how is that  
> callback argument working?
> 
> [[How] to extract the value of an IO action (comment)](https://www.reddit.com/r/haskell/comments/yb09cq/comment/ite79m1)

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [December 1, 2024, 10:10pm UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/17 "2024-12-01T22:10:51Z")

</div>

Sure, but if they did the same in Python they’d end up at the C source of the interpreter, and I don’t think anyone in the Python world wonders how to explain that to beginners. In both cases I think we should explain how I/O “works” by referring to simple examples of it (“synthetically”) rather than by breaking it into simpler pieces (“analytically”). I understand that the synthetic approach is dissatisfying to many Haskellers (some of whom delight in the false notion that “Haskell is just lambda calculus”) but nonetheless I think it would be the better approach for helping beginners understand I/O.

I absolutely agree that the free monad approach has pedagogic value, but I don’t see it as a beginner topic.

I’m not an educator nor a beginner, so my thoughts my be completely off base. I have _been_ a beginner though and when I was I wish someone had said “Don’t bother trying to ‘understand’ `IO` and monads. Just use `do` notation and get comfortable with using them."

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [December 2, 2024, 2:00am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/18 "2024-12-02T02:00:00Z")

</div>

> [@atravers](#):
>
> So if algebraic effects are so amazing…why didn’t they replace dialogues back then?

> [@jaror](#):
>
> I personally think it is quite a shame that representing effectful programs using continuations was so quickly discarded […]

**`<hypothesis>`**

Here’s another look at the defects of dialogue-based I/O:

> [@](#):
>
> (page 5 of 60)
> 
> - It is hard to extend. New input or output facilities can be added only by extending the `Request` and `Response` types, and by changing the “wrapper” program. Ordinary users are unlikely to be able to do this.
> 
> - There is no very close connection between a request and its corresponding response. It is extremely easy to write a program that gets one or more “out of step”.
> 
> - Even if the program remains in step, it is easy to accidentally evaluate the response stream too eagerly, and thereby block emitting a request until the response to that request has arrived – which it won’t.
> 
> [Tackling the Awkward Squad: …](https://www.cs.tufts.edu/comp/150PLD/Papers/awkward.pdf)

Now as algebraic effects, continuations alleviate (if not entirely eliminate!) the second and third problems - it’s _much_ more difficult to write a program that goes _“out of step”_ with the ordering of its effects, or evaluate expressions too early and stop the program from progressing further.

So what about the first [_edited_] problem?

- It is hard to extend. New input or output facilities can be added only by extending the [_continuation type of algebraic effects_], and by changing the “wrapper” program. Ordinary users are unlikely to be able to do this.

Moreover, it would have meant each Haskell implementation keeping its own “wrapper”, to be modified for use with the new type. In contrast, the monadic interface helped to shift that first problem out of those early Haskell implementations:

> [@](#):
>
> - _It is easily extensible_. The key to our implementation is to extend Haskell with a single form that allows one to call an any procedure written in the programming language [C](https://archive.org/details/cprogramminglang00kern), without losing referential transparency (Section 2.3). Using it programmers can readily extend the power of the I/O system, by writing Haskell functions which call operating system procedures.
> 
> [Imperative functional programming](https://dl.acm.org/doi/pdf/10.1145/158511.158524)

This made it possible to define dialogue-based I/O, “wrapper” and all, in Haskell:

> [@](#):
>
> The entire I/O system provided by our compiler  
> is written in Haskell, using the non-standard extensions  
> we describe below. The language’s standard `Dialogue`  
> interface for I/O is supported by providing a function to  
> convert a `Dialogue` into our `IO` monad.
> 
> (second page).

- That function is included in the [`dialogue`](https://hackage.haskell.org/package/dialogue) package: `runDialogue`.

- The [`free-io`](https://github.com/noughtmare/free-io) package has an analogous function: `runIO`.

- Likewise for the [`io-machine`](https://hackage.haskell.org/package/io-machine) package: `runIOMcn`.

So being able to shift that first problem into Haskell was the reason why the monadic interface prevailed over algebraic effects (as continuations).

**`</hypothesis>`**

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [December 10, 2024, 12:05am UTC](https://discourse.haskell.org/t/dialogues-vs-continuations-and-algebraic-effects-to-implement-i-o/10857/19 "2024-12-10T00:05:41Z")

</div>

> [@tomjaguarpaw](#):
>
> […] if they did the same in Python they’d end up at the C source […]

If the FFI was sufficiently-capable, that could also be achieved in standard Haskell:

```haskell
data IO a
foreign import ccall "primUnitIO" unitIO :: a -> IO a
foreign import ccall "primBindIO" bindIO :: IO a -> (a -> IO b) -> IO b

instance Monad IO where
    return = unitIO
    (>>=) = bindIO

instance Functor IO where
    fmap f m = m `bindIO` \ x -> unitIO (f x)

```

- 

> [@](#):
>
> (page 95 of 329)
> 
> The `IO` type serves as a tag for operations (actions) that interact with the outside world. The `IO` type is abstract: no constructors are visible to the user. `IO` is an instance of the `Monad` and `Functor` classes.
> 
> [The Haskell 2010 Report](https://www.haskell.org/definition/haskell2010.pdf)

…[I/O tutorials](https://wiki.haskell.org/IO_tutorials_timeline) _begone!_ `(`and there were celebrations throughout the realm of Haskell `:-)`
