In a way you are right in saying that the same thing can be achieved by adding a parameter; in fact, the effect of any control structure can be be achieved with just function calls, by writing code in continuation-passing style (CPS), for example (it’s actually how effect handlers are compiled by js_of_ocaml, the OCaml to Javascript compller).
The “extra parameter” solution has several drawbacks:
- It is contaminant: any code which calls functions that may perform “effects” have to be written in this weird style. It is also how historically cooperative scheduling has been done in OCaml: since effects handlers were not part of the language before recently, libraries such as Lwt impose the constraint of writing the code in monadic style and to pass functions around as parameters.
- The code is less straightforward than direct-style code, at least to the unaccustomed eye.
- The code is harder to optimize for the compiler. Notably, the high number of functions passed as “continuations” tend to cause a lot of closure allocations.
- Codes written to handle different types of “effects”, or “effect”-handling code and plain code, often don’t mix well. Suppose you need to use an iterator library written in direct, “normal” style: it will probably be very hard to use it in CPS code, for instance, without rewriting it in CPS.