Ответ 1
s
хранит объекты внутри монады ST
от утечки вне монады ST
.
-- This is an error... but let pretend for a moment...
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
. Переменные типа всегда должны появляться внутри 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
-- error --
Конечно, f id
- ошибка, так как она вернет либо список Char
, либо список Int
в зависимости от того, является ли логическое значение true или false. Это просто неправильно, как в примере с ST
.
С другой стороны,, если у вас не было параметра типа s
, тогда все будет набирать проверку просто отлично, хотя код явно довольно фиктивный.
Как работает ST: Реализация, монада ST
на самом деле такая же, как монада IO
, но с немного другим интерфейсом. Когда вы используете монаду ST
, вы фактически получаете unsafePerformIO
или эквивалент за кулисами. Причина, по которой вы можете сделать это безопасно, - это сигнатура типа всех ST
связанных функций, особенно часть с forall
.