モナドは手続き風の書き方ができるだけで破壊的代入を許す変数が導入されてるわけじゃない。
再帰呼び出しで延々と同じパラメタ(例えばコンパイラを関数型で書こうとしたらシンボルテーブルがそういうパラメタ)を
引きずりまわすのを省略したような書き方ができるだけ。
Haskellのモナドは単なるシンタックスシュガー同然のものでモナドを使わない純関数型スタイルに機械的に書き直せるレベル。

それにプログラム全体で使うグローバルな手続き的変数に相当するものをモナドのスタイルで書こうとしたら
プログラム全体を覆う巨大なモナドが必要になって後での機能追加とか削除の変更がやりにくくなる。

それからcall-by-needの場合だと関数の計算量という概念が意味を持たなくなる。
つまり関数が返す値が最終的にどう使われるかで実際にどこまで評価が行われるかが変わるからね。
だから今まで手続き的プログラミング向けに整備されて来たアルゴリズムやプログラムの性能評価の理論やテクニックが使えない。
その点、ML/Lisp系のcall-by-value意味論ベースの言語だとそれらの理論やテクニックがそのまま使える。
(高階関数に関する評価は今までも未整備な部分が多いからそれは新たに整備しないといけないけど
通常のリスト処理とかだと手続き的言語向けの理論やテクニックがほぼそのまま使える)

更にcall-by-need言語では外部割り込み(例えばUNIX系OSでのBREAKキーの信号を拾うとか)の例外は
ゼロ除算や空リストの先頭要素を取り出そうとするような類の内部エラーと違ってモナドでは扱えない。
call-by-need言語の意味論にズルをして汚いものというかゴマカシを持ちこまないと扱えない。
call-by-value言語は手続き的言語と同じで何の問題もなく扱える。

call-by-needは確かに無限データ構造を簡単に扱えるとか便利な点もあるけれど、その為に犠牲にしてる部分の方が
実用的な観点からは大きいと思うなあ。
無限リスト(ストリーム)とか無限木なんて、それらのデモ用のトイプログラムならばともかく、実用プログラムを書く上で
そんなに必要になるものじゃないもの。