Coloured functions are a feature - not a bug, Haskell is full of them, and they are exactly what make STM safe in Haskell, but abandonware in other languages which have tried.
2. The way you call a function depends on its color.
`<-` or `>>=` vs `=`
3. You can only call a red function from within another red function.
This should sound pretty familiar! You can only call an IO function from within another IO function. STM in this case makes a third colour:
IO can call IO functions.
IO can call STM functions. (*)
IO can call pure functions.
STM can call STM functions.
STM can call pure functions.
pure functions can call pure functions.
(*) calling into an STM block from IO is what makes it 'happen for real': it's the `atomically` which has type STM a -> IO a.
Having these coloured functions is what made STM achievable back in the mid-late 2000s, since the mechanism to prevent STM or pure functions from calling IO was already in-place.
Other languages either tried to figure out how to contain the side-effects and gave up, or just released STM and put the onus on the user not to use side effects.