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