Я работаю над проектом Haskell, который включает в себя привязку большого узла: я разбираю сериализованное представление графика, где каждый node находится в некотором смещении в файле и может ссылаться на другой node на его смещение. Поэтому мне нужно создать карту от смещений к узлам во время разбора, которые я могу вернуть себе в блок do rec
.
У меня есть эта работа и вроде бы - сортировка - разумно абстрагирована в трансформатор StateT
-esque monad:
{-# LANGUAGE DoRec, GeneralizedNewtypeDeriving #-}
import qualified Control.Monad.State as S
data Knot s = Knot { past :: s, future :: s }
newtype RecStateT s m a = RecStateT (S.StateT (Knot s) m a) deriving
( Alternative
, Applicative
, Functor
, Monad
, MonadCont
, MonadError e
, MonadFix
, MonadIO
, MonadPlus
, MonadReader r
, MonadTrans
, MonadWriter w )
runRecStateT :: RecStateT s m a -> Knot s -> m (a, Knot s)
runRecStateT (RecStateT st) = S.runStateT st
tie :: MonadFix m => RecStateT s m a -> s -> m (a, s)
tie m s = do
rec (a, Knot s' _) <- runRecStateT m (Knot s s')
return (a, s')
get :: Monad m => RecStateT s m (Knot s)
get = RecStateT S.get
put :: Monad m => s -> RecStateT s m ()
put s = RecStateT $ S.modify $ \ ~(Knot _ s') -> Knot s s'
Функция tie
- это то, где происходит волшебство: вызов runRecStateT
вызывает значение и состояние, которое я кормлю его как свое собственное будущее. Обратите внимание, что get
позволяет вам читать как из прошлого, так и из будущего состояния, но put
позволяет вам изменять "настоящее".
Вопрос 1: Это похоже на достойный способ реализовать эту привязку к узлу? Или, еще лучше, кто-то внедрил общее решение для этого, что я упустил, когда шпионил через Hackage? Я немного погубил голову над монадой Cont
, так как она казалась, возможно, более элегантной (см. аналогичную запись от Дэна Бертона), но я просто не мог работать это.
Полностью субъективный вопрос 2: я не совсем в восторге от того, как мой код вызова заканчивается:
do
Knot past future <- get
let {- ... -} = past
{- ... -} = future
node = {- ... -}
put $ {- ... -}
return node
Детали реализации здесь опущены, очевидно, важным моментом является то, что я должен получить состояние past
и future
, сопоставить шаблон с ними внутри привязки let (или явно сделать предыдущий шаблон ленивым), чтобы извлечь все, что я а затем создайте мой node, обновите мое состояние и, наконец, верните node. Кажется излишне подробным, и мне особенно не нравится, как легко случайно создать шаблон, который выделяет состояния past
и future
. Итак, может ли кто-нибудь подумать о более удобном интерфейсе?