sЗберігає об'єкти всередині STмонади від витоку назовні з STмонади.
let a = runST $ newSTRef (15 :: Int)
b = runST $ writeSTRef a 20
c = runST $ readSTRef a
in b `seq` c
Добре, це помилка типу (що добре! Ми не хочемо STRefвитікати за межі початкового обчислення!). Це помилка типу через додаткове s. Пам'ятайте, що runSTмає підпис:
runST :: (forall s . ST s a) -> a
Це означає, що sобчислення, яке ви запускаєте, не повинно мати обмежень на нього. Отже, коли ви намагаєтесь оцінити a:
a = runST (newSTRef (15 :: Int) :: forall s. ST s (STRef s Int))
Результат мав би тип STRef s Int, що є неправильним, оскільки s"втекло" за межі forallвходу runST. Змінні типу завжди повинні відображатися всередині a forall, а Haskell дозволяє неявні forallквантори скрізь. Просто немає правила, яке дозволяє вам змістовно з’ясувати тип повернення a.
Ще один приклад з forall: Щоб наочно показати, чому ви не можете дозволити речам уникнути forall, ось простіший приклад:
f :: (forall a. [a] -> b) -> Bool -> b
f g flag =
if flag
then g "abcd"
else g [1,2]
> :t f length
f length :: Bool -> Int
> :t f id
Звичайно f id, це помилка, оскільки вона повертає або список, Charабо список Intзалежно від того, логічне значення є істинним чи хибним. Це просто неправильно, як і приклад з ST.
З іншого боку, якщо у вас не було sпараметра type, то все перевірятиме тип просто чудово, хоча код, очевидно, досить неправдивий.
Як насправді працює ST: з точки STзору реалізації, монада насправді така ж, як IOмонада, але з дещо іншим інтерфейсом. Коли ви використовуєте STмонаду, яку фактично отримуєте, unsafePerformIOабо її еквівалент, за лаштунками. Причиною того, що ви можете зробити це безпечно, є STтипова підпис усіх пов'язаних функцій, особливо частини з forall.