Я работаю над реализацией алгоритма UCT в Haskell, который требует изрядного количества подтасовка данных. Не вдаваясь в подробности, это алгоритм моделирования, в котором на каждом «шаге» листовой узел в дереве поиска выбирается на основе некоторых статистических свойств, на этом листе создается новый дочерний узел, а статистика соответствует новый лист и все его предки обновляются.
Учитывая все это жонглирование, я недостаточно сообразителен, чтобы понять, как сделать все дерево поиска красивой неизменяемой структурой данных а-ля Окасаки . Вместо этого я немного поигрался с монадой ST
, создав структуры, состоящие из изменяемых STRef
s. Надуманный пример (не связанный с UCT):
import Control.Monad
import Control.Monad.ST
import Data.STRef
data STRefPair s a b = STRefPair { left :: STRef s a, right :: STRef s b }
mkStRefPair :: a -> b -> ST s (STRefPair s a b)
mkStRefPair a b = do
a' <- newSTRef a
b' <- newSTRef b
return $ STRefPair a' b'
derp :: (Num a, Num b) => STRefPair s a b -> ST s ()
derp p = do
modifySTRef (left p) (\x -> x + 1)
modifySTRef (right p) (\x -> x - 1)
herp :: (Num a, Num b) => (a, b)
herp = runST $ do
p <- mkStRefPair 0 0
replicateM_ 10 $ derp p
a <- readSTRef $ left p
b <- readSTRef $ right p
return (a, b)
main = print herp -- should print (10, -10)
Очевидно, что этот конкретный пример было бы намного проще написать без использования ST
, но, надеюсь, ясно, куда я иду с этим ...Если бы я применил такой стиль к моему варианту использования UCT, это неправильно?
Кто-то задал аналогичный вопрос здесь пару лет назад, но я думаю, что мой вопрос немного другой ... У меня нет проблем с использованием монад для инкапсуляции изменяемого состояния, когда это необходимо, но меня привлекает именно это предложение «когда необходимо». Меня беспокоит, что я преждевременно возвращаюсь к объектно-ориентированному мышлению, когда у меня есть куча объектов с геттерами и сеттерами. Не совсем идиоматический Haskell ...
С другой стороны, если это разумный стиль кодирования для некоторого набора проблем, я думаю, у меня будет вопрос: есть ли какие-нибудь хорошо известные способы сохранить это код читабельный и ремонтопригодный? Меня как бы выбивают из колеи все явные операции чтения и записи, и особенно неприятно то, что мне приходится переводить из моих STRef
структур внутри монады ST
в изоморфные, но неизменяемые структуры вне.
Это может быть действительно трудно сказать, когда использование ST целесообразно. Я бы посоветовал вам сделать это с ST и без ST (не обязательно в таком порядке). Держите не-ST версию простой; использование ST следует рассматривать как оптимизацию, и вы не хотите этого делать, пока не поймете, что вам это нужно.
Использование монады ST обычно (но не всегда) в качестве оптимизации. Для любой оптимизации я применяю ту же процедуру:
Другой известный мне вариант использования - это альтернатива государственной монаде. Ключевым отличием является то, что в монаде состояния тип всех хранимых данных указывается сверху вниз, а в монаде ST - снизу вверх. Есть случаи, когда это полезно.