haskell - 如何使用非 IO "exterior"构建 Monad,但 IO "interior"?

标签 haskell monads

我正在尝试编写一个呈现一些 HTML 的 Monad,同时跟踪(和缓存)一些特定的函数调用。这是我尝试过的:

data TemplateM a = TemplateM
  { templateCache :: ![(Text, Text)]
  , templateResult :: !(IO a)
  }

下面是我打算如何使用它:

renderCached :: Text -> TemplateM Text
renderCached k = 
  -- lookup templateCache from the monadic context, if it lacks the key, 
  -- then fetch the key from an external data source (which is where the
  -- "IO interior" comes from, and store it in templateCache (monadic context)

值得注意的是,我不希望通过 liftTemplateM 中执行任意 IO 操作,liftIO 等。 TemplateM 中应该发生的唯一 IO 是通过 renderCached 函数从缓存中获取一些内容。

我能够为此定义 FunctorApplicative 实例,但完全无法使用 Monad 实例。这是我得到的结果:

instance Functor TemplateM where
  {-# INLINE fmap #-}
  fmap fn tmpl = tmpl{templateResult=fmap fn (templateResult tmpl)}

instance Applicative TemplateM where
  {-# INLINE pure #-}
  pure x = TemplateM
    { templateCache = []
    , templateResult = pure x
    }

  {-# INLINE (<*>) #-}
  fn <*> f =
    let fnCache = templateCache fn
        fnFunction = templateResult fn
        fCache = templateCache f
        fResult = templateResult f
    in TemplateM { templateCache = fnCache <> fCache
                 , templateResult = fnFunction <*> fResult
                 }

有没有什么方法可以为此编写 Monad 实例而不将 IO 内部结构暴露给外界?

最佳答案

我已经在 ReaderT 之上制定了一个解决方案,但我真的想让我最初的想法发挥作用:

newtype TemplateM a = TemplateM { unTemplateM :: ReaderT (IORef [(Text, Text)]) IO a } deriving (Functor, Applicative, Monad)

renderCached :: Text -> TemplateM Text
renderCached k = TemplateM $ do
  -- this is just dummy code. The actual cache lookup has not
  -- been implemented, but the types align
  v <- pure $ "rendered template for " <> k
  cacheRef <- ask
  atomicModifyIORef' cacheRef (\x -> ((k, v):x, ()))
  pure v

runTemplateM :: [(Text, Text)] 
             -> TemplateM a 
             -> IO ([(Text, Text)], a)
runTemplateM initialCache x = do
  initialCacheRef <- newIORef initialCache
  (flip runReaderT) initialCacheRef $ do
    res <- unTemplateM x
    ref <- ask
    finalCache <- readIORef ref
    pure (finalCache, res)

关于haskell - 如何使用非 IO "exterior"构建 Monad,但 IO "interior"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62631211/

相关文章:

scala - 为无形 hlist 定义 scalaz monad 实例

haskell - 在 Haskell 中存储多态回调

haskell - 变形金刚下的转型

haskell - 绑定(bind)和连接之间有什么关系?

performance - 使用 Data.ByteString 实现 unix 的 "cat"程序的 Haskell 性能

haskell - 如何在 D 中实现 Haskell *Maybe* 构造?

haskell - Haskell 中函数的类型签名

scala - 组合偏函数

haskell - 自动将 Either 提升到 exceptT

class - 无法使 String 成为 Haskell 中的类的实例