Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatically return a Maybe at the end of a chain [duplicate]

I have the following code snippet, which kinda works:

launchTask :: (DeviceRepo m) => TaskSpec -> m (Maybe Task)
launchTask taskSpec@TaskSpec { taskSpecImage = image
                             , taskSpecRequirement = requirement
                             } = do
  mayDevice <- getDeviceMatchingRequirement requirement
  case mayDevice of
    Nothing -> return Nothing
    Just device -> do
      mayContainer <- createContainer device requirement
      case mayContainer of
        Nothing -> return Nothing
        Just container ->
          return $
          Just
            Task
              { taskName = pack image
              , taskStatus = TaskStatusRunning
              , taskSpec = taskSpec
              , taskContainerId = containerId container
              }

However, I feel that this is not very idiomatic. What's the right way to stack the main calculation (from DeviceRepo m) and perhaps a Maybe monad so that if any of the values are missing then Nothing is instantly returned?

Edit: My attempt with MaybeT, but the type doesn't match

launchTask2 :: (DeviceRepo m) => TaskSpec -> m (Maybe Task)
launchTask2 taskSpec@TaskSpec { taskSpecImage = image
                              , taskSpecRequirement = requirement
                              } = runMaybeT launcher
  where
    launcher :: MaybeT m Task
    launcher =
      getDeviceMatchingRequirement requirement >>= \device ->
        createContainer device requirement >>= \container ->
          return
            Task
              { taskName = pack image
              , taskStatus = TaskStatusRunning
              , taskSpec = taskSpec
              , taskContainerId = containerId container
              }
    • Couldn't match expected type ‘Device’
                  with actual type ‘Maybe Device’
    • In the first argument of ‘createContainer’, namely ‘device’
      In the first argument of ‘(>>=)’, namely
        ‘createContainer device requirement’
      In the expression:
        createContainer device requirement
          >>=
            \ container
              -> return
                   Task
                     {taskName = pack image, taskStatus = TaskStatusRunning,
                      taskSpec = taskSpec, taskContainerId = containerId container}
   |
58 |         createContainer device requirement >>= \container ->
   |                         ^^^^^^
like image 737
thoaionline Avatar asked Oct 28 '25 08:10

thoaionline


1 Answers

You are probably looking for the MaybeT monad transformer from the transformers package:

import Control.Monad.Trans.Maybe(MaybeT(MaybeT))

launchTask' :: DeviceRepo m => TaskSpec -> MaybeT m Task
launchTask' taskSpec@TaskSpec { taskSpecImage = image, taskSpecRequirement = requirement } = do
    device <- MaybeT (getDeviceMatchingRequirement requirement)
    container <- MaybeT (createContainer device requirement)
    return Task
        { taskName = pack image
        , taskStatus = TaskStatusRunning
        , taskSpec = taskSpec
        , taskContainerId = containerId container
        }

This is thus a function MaybeT m Task, you can use runMaybeT to transform the MaybeT m a into an m (Maybe a):

import Control.Monad.Trans.Maybe(runMaybeT)

lanchTask :: DeviceRepo m => TaskSpec -> m (Maybe Task)
launchTask = runMaybeT . lanchTask'
like image 175
Willem Van Onsem Avatar answered Oct 30 '25 07:10

Willem Van Onsem



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!