有3个单子结果时的Haskell的mapM

时间:2018-07-19 13:48:33

标签: haskell monads persistent

现在我一直在运行Haskell代码,我觉得我应该能够使用mapM来使Monads的操作更整洁,但是如果我有两个类型为m (t a)的计算,并且也许还有另一种使用t的操作,类似于a -> t a(通常是Maybe)……说我有这个持久代码:

findDealership :: MonadIO m => Key Vehicle -> SqlPersistT m (Maybe (Entity Dealership))
findDealership vehicleKey = mapM selectDealership (getVehicleDealership vehicleKey)

getVehicleDealership :: MonadIO m => Key Vehicle -> SqlPersistT m (Maybe (Key Dealership))
getVehicleDealership vehicleKey =
  (\x -> x >>= vehicleDealershipId . entityVal) <$> getEntity vehicleKey

selectDealership :: MonadIO m => Key Dealership -> SqlPersistT m (Maybe (Entity Dealership))
selectDealership dealershipKey = selectFirst [DealershipId ==. dealershipKey] []

如果vehicleDealershipId是类型Maybe (Key Dealership)的字段,则上面的代码将无法编译,并且我无法计算出>>=和{{1}的正确组合}的...。我觉得这个问题有点像一个mapM所解决的问题,只是那里存在另一层单子。。。你可以以mapM结尾需要将内部类型完全弄平...

上述代码的错误输出:

SqlPersistT m (Maybe (Maybe (Maybe (Key Dealership))))

和:

    • Couldn't match type ‘Control.Monad.Trans.Reader.ReaderT
                             SqlBackend m0’
                     with ‘Maybe’
      Expected type: SqlPersistT m (Maybe (Entity Dealership))
        Actual type: Control.Monad.Trans.Reader.ReaderT
                       SqlBackend
                       m
                       (Control.Monad.Trans.Reader.ReaderT
                          SqlBackend m0 (Maybe (Entity Dealership)))
    • In the expression:
        mapM selectDealership $ getVehicleDealership vehicleKey
      In an equation for ‘findDealership’:
          findDealership vehicleKey
            = mapM selectDealership $ getVehicleDealership vehicleKey
   |
46 | findDealership vehicleKey = mapM selectDealership $ getVehicleDealership vehicleKey
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

2 个答案:

答案 0 :(得分:3)

当您要展平多个Maybe时,可以使用join。它适用于任何Monad,但最常用于Maybe

getVehicleDealership :: MonadIO m => Key Vehicle -> SqlPersistT m (Maybe (Key Dealership))
getVehicleDealership vehicleKey =
    join . fmap (vehicleDealershipId . entityVal) <$> getEntity vehicleKey

mapM(又称traverse)在许多情况下很有用,但我认为它在这里并不适用。 Maybe的显示顺序无关紧要,而您想要的是SqlPersistT m (Maybe a),而不是Maybe (SqlPersistT m a)

答案 1 :(得分:0)

我现在意识到,我在这里想要的是MaybeT

我的示例可以像这样使用 MaybeT 重写:

findDealership :: MonadIO m => Key Vehicle -> SqlPersistT m (Maybe (Entity Dealership))
findDealership vehicleKey = runMaybeT $ do
  dealershipKey <- MaybeT (getVehicleDealership vehicleKey) 
  selectDealership dealershipKey

getVehicleDealership :: MonadIO m => Key Vehicle -> SqlPersistT m (Maybe (Key Dealership))
getVehicleDealership vehicleKey = runMaybeT $ do
  vehicleEntity <- MaybeT (getEntity vehicleKey)
  hoistMaybe $ vehicleDealershipId (entityVal vehicleEntity)

selectDealership :: MonadIO m => Key Dealership -> SqlPersistT m (Maybe (Entity Dealership))
selectDealership dealershipKey = 
  selectFirst [DealershipId ==. dealershipKey] []