Як працює монада ST?


77

Я розумію, що монада ST - це щось на зразок маленького брата IO, який, у свою чергу, є державною монадою з доданою RealWorldмагією. Я можу уявити собі стан , і я можу уявити , що RealWorld який - то чином ввести в IO, але кожен раз , коли я пишу тип підпис з ST монади плутає мене.STs

Візьмемо, наприклад, ST s (STArray s a b). Як sтам працює? Чи він просто використовується для створення якоїсь штучної залежності даних між обчисленнями, не маючи змоги посилатися на них як на стани в монаді штатів (завдяки forall)?

Я просто викидаю ідеї і справді був би вдячний комусь більш обізнаному, ніж я, щоб пояснити мені це.


Якщо ви шукаєте "ST монада", є запис, який відповідає на моє запитання як побічну відповідь на питання про STArrays. Не впевнений, що моє запитання тут є дублікатом зараз. stackoverflow.com/questions/8197032/…
Девід

2
Це так спокусливо просто відповісти "Так". на ваше запитання. :)
AndrewC

Думаю, додавання посилання на запитання та його закриття а) полегшить пошук цього в майбутньому та б) заощадить деякий час відповідачів.
Девід

2
Я вкажу вам на моє видання про типи 2-го рангу , яке також має допомогти вам зрозуміти STмонаду. Як тільки ви зрозумієте, як типи rank-2 можуть контролювати "хто вибирає" тип, що використовується для змінної типу, це повинно допомогти вам зрозуміти, як STоперації використовують rank-2, щоб запобігти незаконному використанню його обчислень.
Luis Casillas

Ви можете писати a :: ST Int Int; a = return 2 ST- це цілком звичайна монада стану, за винятком того, що вона використовує невпаковану пару як вихід функції функції, State# s -> (# State# s, a #)що робить недоцільним мати справу з нею. Таємниця цілком у тому, runSTщо має тип 2 рангу, хоча сам ST не є.
заява

Відповіді:


75

sЗберігає об'єкти всередині STмонади від витоку назовні з STмонади.

-- This is an error... but let's 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. Змінні типу завжди повинні відображатися всередині 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
-- error --

Звичайно f id, це помилка, оскільки вона повертає або список, Charабо список Intзалежно від того, логічне значення є істинним чи хибним. Це просто неправильно, як і приклад з ST.

З іншого боку, якщо у вас не було sпараметра type, то все перевірятиме тип просто чудово, хоча код, очевидно, досить неправдивий.

Як насправді працює ST: з точки STзору реалізації, монада насправді така ж, як IOмонада, але з дещо іншим інтерфейсом. Коли ви використовуєте STмонаду, яку фактично отримуєте, unsafePerformIOабо її еквівалент, за лаштунками. Причиною того, що ви можете зробити це безпечно, є STтипова підпис усіх пов'язаних функцій, особливо частини з forall.


Чи можете ви "порушити" безпеку ST, використовуючи realWorld#безпосередньо в коді клієнта, щоб імітувати runSTRep? Звичайно, це погана ідея, але мені цікаво, якими є засоби захисту, крім того, realworld#що це очевидно небезпечне значення для використання в програмі. hackage.haskell.org/package/base-4.6.0.1/docs/src/…
misterbee

@misterbee Так , і це навіть не обов'язково небезпечно. Пам’ятайте, sіснує лише на рівні типу.
PyRulez

28

Це sлише хакерство, яке змушує систему шрифтів зупиняти вас у справах, які були б небезпечними. Він нічого не "робить" під час виконання; він просто змушує перевірку типу відкидати програми, які роблять сумнівні речі. (Це так званий фантомний тип , річ, яка існує лише в голові перевірки типу, і нічого не впливає на час виконання.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.