Изменить ST-зависимую среду в ReaderT - проблема с функцией `local` - PullRequest
0 голосов
/ 26 января 2019

Этот вопрос является продолжением этой темы: https://stackoverflow.com/a/54317095/4400060

Я спрашивал там о переносе STRef в окружение ReaderT и выполнении ST-действий под ним. Моя настройка теперь выглядит так:

import Data.HashTable.ST.Cuckoo as HT

-- |Environment for Comp
newtype Env s = Env { dataspace :: HashTable s Int Data
                    , namespace :: Map Name Int }

-- |Main computation monad
newtype Comp a = Comp (forall s. ReaderT (Env s) (ST s) a)


-- |Evaluate computation
runComp (Comp c) = runST $ do
    ds <- HT.new
    runReaderT c (Env ds empty)


-- |Perform an action on `dataspace` hashmap
onDataspace :: (forall s. HashTable s Int Data -> ST s a) -> Comp a
onDataspace f = Comp $ asks dataspace >>= lift . f

И в целом работает круто - я могу свободно получить или изменить dataspace на месте. Однако, когда я добавил неизменный namespace, я начал бороться. Мне нужна функция, запускающая действие Comp с обновленным namespace таким образом, чтобы он не влиял на пространства имен дальнейших вычислений - именно то, что делает local.

Прежде всего я хотел написать MonadReader экземпляр для Comp, однако я столкнулся с фантомным типом ST и получил illegal instance ошибку:

instance MonadReader (Env s) Comp where {}
instance MonadReader (forall s. Env s) Comp where {}
instance forall s. MonadReader (Env s) Comp where {}

Полное сообщение об ошибке:

Illegal instance declaration for
     ‘MonadReader (EvalEnv s) Evaluator’
     The coverage condition fails in class ‘MonadReader’
       for functional dependency: ‘m -> r’
     Reason: lhs type ‘Evaluator’
       does not determine rhs type ‘EvalEnv s’
     Un-determined variable: s

Я понимаю эту ошибку, но не вижу способа ее обойти. Честно говоря, мне не нужна полная функция local. Мне нужно только иметь возможность запускать Comp с разными namespace, но одинаковыми dataspace.

Лучшим решением будет предоставить полный MonadReader экземпляр. Я знаю, что это может быть невозможно, поэтому в качестве обходного пути мне бы хотелось иметь функцию

withNs :: Map Name Int -> Comp a -> Comp a

Подводя итог: я хочу иметь возможность запускать Comp с измененным namespace, оставляя dataspace без изменений в качестве ссылки, сохраняя все изменения под ним.

Как это сделать? Я могу принять изменение моей первоначальной настройки, если это необходимо.

1 Ответ

0 голосов
/ 26 января 2019

Параметр области действия s из ST должен оставаться снаружи:

newtype Comp s a = Comp (ReaderT (Env s) (ST s) a)

Единственное место, где вам нужен тип более высокого ранга, - это вызов runST.

runComp :: (forall s. Comp s a) -> a
runComp = runST $ do
  ds <- HT.new
  runReaderT c (Env ds empty)

В любом другом месте вы можете просто быть параметрическими в s.

doStuff :: Comp s Bool
doMoreStuff :: Comp s Int

Тогда можно написать экземпляр MonadReader:

instance MonadReader (Env s) (Comp s) where
  ...
...