Что происходит в подписи этого типа? (Vector.Mutable модификаторы в Haskell)

У взаимозаменяемых векторов в Haskell есть три мутатора на уровне элементов:

read :: PrimMonad m => MVector (PrimState m) a -> Int -> m a
write :: PrimMonad m => MVector (PrimState m) a -> Int -> a -> m ()
swap :: PrimMonad m => MVector (PrimState m) a -> Int -> Int -> m ()

Теперь я могу использовать эти штрафы -

import Data.Vector
import Data.Vector.Mutable 
import Control.Monad.ST
import Control.Monad.Primitive 

incrAt :: Vector Double -> Int -> Vector Double
incrAt vec i = runST $ do
  mvec <- thaw vec
  oldval <- read mvec i
  write mvec i (oldval + 1)
  freeze mvec

Но что здесь происходит? Что такое PrimMonad? И есть PrimState конструктор?

Я понимаю, что здесь есть привязка к монаде класса PrimMonad. thaw возвращает m (MVector (PrimState m) a), где m является PrimMonad... но монада содержит себя? Почему m находится в контексте другого m?

Я вижу, что все это в основном связано с этим PrimState или PrimMonad, но я не вижу, как это связано с изменяемыми/сохраняемыми векторами. Есть ли что-то особенное в тех классах, которые позволяют хранить состояние?

Спасибо за ваше время!

Ответ 1

Я думаю, что вы используете векторный пакет , как в

import Data.Vector.Mutable

Следуя за классом типа PrimMonad, он приводит к деталям низкого уровня; вещь, которую нужно заметить, это два экземпляра:

instance PrimMonad IO where ...
instance PrimMonad (ST s) where ...

Итак, (PrimMonad m) - это просто способ сказать m либо IO, либо (ST s). Это две основные монады, в которых Haskell настроен так, чтобы вы могли мутировать память. Чтобы быть ясным, m является конструктором типа и применение m к типу типа Int дает тип: m Int.

Чтобы подчеркнуть: IO и (ST s) являются особенными, поскольку они позволяют вам "сохранять состояние", используя эту способность для изменения фактической памяти. Они раскрывают эту возможность в необработанной форме, которую скрывает остальная часть Haskell.

Теперь PrimState - это новая вещь: связанный тип данных. В классе типа PrimMonad есть объявление:

-- | Class of primitive state-transformer monads
class Monad m => PrimMonad m where
  -- | State token type
  type PrimState m

Тип, который (PrimState m) будет в вашем коде, зависит от того, что ему присвоил экземпляр для (PrimMonad m).

instance PrimMonad IO where
  type PrimState IO = RealWorld

instance PrimMonad (ST s) where
  type PrimState (ST s) = s

Тип RealWorld - это внутренняя деталь реализации низкого уровня ввода-вывода в GHC. Тип s, привязанный к (ST s), - это трюк экзистенциального типа, который позволяет runST доказать, что ничто не изменяемое не удалось избежать монады (ST s).

Чтобы сделать тот же самый код в IO и (ST s), тип PrimMonad type-class (с ассоциированным PrimState) используется для обеспечения перегрузки ad-hoc.