Карта HaskellM, когда есть 3 монадических результата

Время от времени я продолжаю сталкиваться с кодом на Haskell, где я чувствую, что должен быть в состоянии использовать mapM сделать работу над монадами чище, но если у меня есть два вычисления типа 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)приведенный выше код не скомпилируется, и я не могу определить правильную комбинацию >>=и mapMS.... Я чувствую, что эта проблема немного похожа на 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
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

а также:

    • Couldn't match type ‘Maybe (Key Dealership)’
                     with ‘Key Dealership’
      Expected type: Control.Monad.Trans.Reader.ReaderT
                       SqlBackend m0 (Key Dealership)
        Actual type: SqlPersistT m0 (Maybe (Key Dealership))
    • In the second argument of ‘($)’, namely
        ‘getVehicleDealership vehicleKey’
      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 ответа

Когда вы хотите сгладить несколько Maybeс, вы можете использовать join, Это работает для любого Monad, но чаще всего подходит для Maybe,

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

mapM (AKA traverse) полезен во многих ситуациях, но я не думаю, что это применимо здесь. Неважно, какой порядок Maybeпоявляются, и вы хотите SqlPersistT m (Maybe a)не Maybe (SqlPersistT m a),

Теперь я понял, что то, что я хотел, было здесь.

Мой пример можно переписать, используя 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] []
Другие вопросы по тегам