Предыстория: http://thesz.livejournal.com/950788.html?thread=7471620#t7471620
Т.е. предполагалось, что любой
Однако, простой пример
будет в зависимости от настроения то выводить
Тогда вместо
Какие могут быть подводные камни у такой реализации?
Ну, и самое главное. Я наконец понял, что перевести
Т.е. предполагалось, что любой
mdo можно развернуть в letrec (т.е. do без mfix).Однако, простой пример
mdo
tid1 <- forkOS (print tid2)
tid2 <- forkOS (print tid1)
return ()
будет в зависимости от настроения то выводить
ThreadId, то показывать <<loop>>. Всё потому, что для хранения переменной используется IORef. Можно переписать mfix для IO, используя MVar:
newtype MyIO a = MyIO { runMyIO :: IO a }
deriving (Functor, Monad, MonadIO)
instance MonadFix MyIO where
mfix f = MyIO $ do
ref <- newEmptyMVar
vars <- unsafeInterleaveIO $ takeMVar ref
realVars <- runMyIO $ f vars
putMVar ref realVars
return realVars
Тогда вместо
<<loop>> просто висим в ожидании MVar. Зато наш пример начинает работать по человечески всегда:
myMain :: MyIO ()
myMain = mdo
tid1 <- liftIO $ forkOS (print tid2)
tid2 <- liftIO $ forkOS (print tid1)
return ()
Какие могут быть подводные камни у такой реализации?
Ну, и самое главное. Я наконец понял, что перевести
mdo в letrec не всегда возможно. Например, здесь. Если мы просто завернём все tid в монаду, то получим дли-и-и-инную цепочку thunk-ов, которую не сможем развернуть - stack overflow, однако! Если мы сделаем IO ленивым (тот же unsafeInterleaveIO), то получим многократное выполнение. Т.е. нам придётся вернуться к реализации ленивого IO с сохранением результата через какую нибудь изменяемую переменную. Ленивый - чтобы не зациклилось, сохранять результат - чтобы не перезапускалось.
no subject
Date: 2009-04-06 06:07 pm (UTC)Где ИРЛ может встретиться такая извращённая композиция вычислений?
no subject
Date: 2009-04-06 07:55 pm (UTC)В исходной статье (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.6.5172) на эту тему первые примеры как раз оттуда.