Strictly speaking, the issue of Haskell threads only being preempted on memory allocation isn't a "language designer bowing to the inevitable", since its not a language feature, its a feature of the GHC implementation of Concurrent Haskell [1] (you have to have some heuristic like this if you are using green threads, but there's no reason Concurrent Haskell
couldn't be implemented with native threads, but M:N green threads have advantages in practice, even with pathological cases that rarely occur in real programs.)
> although I'd argue that having the potential to yield as a side-effect of allocation is a sub-optimal choice for a side-effect-free language...
Preemption isn't a side-effect in the sense in which Haskell is side-effect free even in the single-threaded case, since timing isn't guaranteed to start with (and, particularly, a single-threaded Haskell program can be preempted by the host OS at any time, based on any heuristic applied by the host OS, and for any amount of time), and to extent that preemption effects semantics, it only effects the semantics of things like IO actions which are not, by design, side-effect.
> I presume there must be an explicit instruction also that permits a yield to take place?
Yes, Concurrent Haskell includes an explicit yield.
[1] https://hackage.haskell.org/package/base-4.7.0.0/docs/Contro...