ReaderT Design Pattern Reflection

:set +m
:set -package stm
:set -package transforms
:set -package lifted-async
:set -package mtl

{-# LANGUAGE FlexibleContexts #-}
import Control.Concurrent.Async.Lifted.Safe
import Control.Monad.Reader
import Control.Concurrent.STM

modify :: (MonadReader (TVar Int) m, MonadIO m)
       => (Int -> Int)
       -> m ()
modify f = do
  ref <- ask
  liftIO $ atomically $ modifyTVar' ref f

ref <- newTVarIO 4
runReaderT (concurrently (modify (+ 1)) (modify (+ 2))) ref
readTVarIO ref >>= print
4

org mode の source block では 4 になってしまう。正しくは 7

cabal ではうまくいく。

#!/usr/bin/env cabal
{- cabal:
build-depends: base, stm, lifted-async, transformers, mtl
-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Concurrent.Async.Lifted.Safe
import Control.Monad.Reader
import Control.Concurrent.STM

modify :: (MonadReader (TVar Int) m, MonadIO m)
       => (Int -> Int)
       -> m ()
modify f = do
  ref <- ask
  liftIO $ atomically $ modifyTVar' ref f

main :: IO ()
main = do
  ref <- newTVarIO 4
  runReaderT (concurrently (modify (+ 1)) (modify (+ 2))) ref
  readTVarIO ref >>= print
7

応用として、 m() ではなく、 m(Maybe Int) とするとこんな風になる。

#!/usr/bin/env cabal
{- cabal:
build-depends: base, stm, lifted-async, transformers, mtl
-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Concurrent.Async.Lifted.Safe
import Control.Monad.Reader
import Control.Concurrent.STM

modify :: (MonadReader (TVar Int) m, MonadIO m)
       => (Int -> Int)
       -> m (Maybe Int)
modify f = do
  ref <- ask
  liftIO $ atomically $ do
    modifyTVar' ref f
    Just <$> readTVar ref

main :: IO ()
main = do
  ref <- newTVarIO 4
  res <- runReaderT (concurrently (modify (+ 1)) (modify (+ 2))) ref
  readTVarIO ref >>= print
  print res
(Just 5,Just 7)

concurrently の type signature は

concurrently :: forall m a b. (MonadBaseControl IO m, Forall (Pure m)) => m a -> m b -> m (a, b)

なので、結果は tuple でよい。

Reference

https://www.fpcomplete.com/blog/readert-design-pattern/