haskell - 如何编写使用 ReaderT 和 Either 运行的函数?

标签 haskell monads functor monad-transformers

是否可以将以下函数重写为单行函数?

action :: NewTenant -> AppM (Either TenantCreationError Tenant)
action newTenant = (createTenant newTenant) >>= \case
  Left x -> return $ Left x
  Right y -> do
    t <- activateTenant $ (y ^. key)
    return $ Right t

type AppM = ReaderT AppConfig IO
createTenant :: NewTenant -> AppM (Either TenantCreationError Tenant)
activateTenant :: TenantId -> AppM Tenant

最佳答案

可能最好的方法是包含 ExceptT或类似的 AppM单子(monad)。然后你将为 createTenant 赋予新类型和activateTenant :

createTenant :: NewTenant -> AppM Tenant
activateTenant :: TenantId -> AppM Tenant

action :: NewTenant -> AppM Tenant
action = activateTenant . view key <=< createTenant

您可以使用 ExceptT 将旧函数转换为新的 monad 堆栈。 (对于 createTenant )和 lift (对于 activateTenant )。

如果由于某种原因这种方法不可行,那么您可以使代码适本地不可读:

action = createTenant >=> either (return . Left) (\y -> Right <$> activateTenant (y ^. key))

推杆的一个缺点ExceptT在你的AppM monad 的缺点是你无法区分可能失败和不能失败的操作。如果这对您很重要,您有几个选择。

  1. 使用ExcepT本地仅用于其实例。你会保留AppM按原样和 createTenant 的类型和activateTenant按原样,但写

    action newTenant = runExceptT $ do
        y <- ExcepT (createTenant newTenant)
        lift (activateTenant (y ^. key))
    

    或其等效的一行:

    action n = runExcepT (ExceptT (createTenant n) >>= lift . activateTenant . view key)
    
  2. 使您的操作的效果具有多态性。您仍然会包括 ExceptTAppM monad,但是 createTenant 的类型和activateTenant现在将是

    createTenant :: (MonadReader AppConfig m, MonadIO m, MonadThrow TenantCreationError m)
                 => NewTenant -> m Tenant
    activateTenant :: (MonadReader AppConfig m, MonadIO m)
                   => TenantId -> m Tenant
    
    action :: (MonadReader AppConfig m, MonadIO m, MonadThrow TenantCreationError m)
           => NewTenant => m Tenant
    action = activateTenant . view key <=< createTenant
    

    然后您将特别能够给出 action单态型AppM Tenant ;从 activateTenant 的类型中仍然可以清楚地看出它不会失败。而且,它会让你有机会说出以前不能说的话;例如如果newTenant不需要做IO您可以通过删除 MonadIO m 来表明这一点来自其类型的限制。您可以通过为您希望最常使用的组合定义类型同义词来恢复短类型签名,例如

    type ConfigIO m = (MonadReader AppConfig m, MonadIO m)
    type Failable m = (ConfigIO m, MonadThrow TenantCreationError m)
    createTenant :: Failable m => NewTenant -> m Tenant
    activateTenant :: ConfigIO m => TenantId -> m Tenant
    

关于haskell - 如何编写使用 ReaderT 和 Either 运行的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39689887/

相关文章:

typescript - Maybe monad 在 TypeScript 中有何用处?

haskell - 如何向实例声明添加 (Vector v a) 约束?

Haskell:括号有效,但 $ 抛出错误

scala - 是否有任何将元组视为 monad 的 Scala 库

list - Haskell Monad - 列表中的 Monad 如何工作?

generics - Seq.iter 比 native 映射慢 : any generic solution?

c++ - 如何迭代派生函数对象列表并访问派生对象成员变量

c++ - 我可以将 CRTP 与虚函数或仿函数一起用于访问者算法以容忍类的更改吗

haskell - Cabal:奇怪的错误消息+缺乏文档

haskell - 未能混合 {,non-}point-free 函数定义子句