acrognale
acrognale

Reputation: 300

Unsure how to assign monadic value to Aeson pair

Consider the following code:

    S.get "/budget/:bid/month/:mnth" $ do
      mbid <- param "bid"
      (budget :: Maybe Budget) <- liftIO $ getBudget $ toSqlKey mbid
      (categories :: [Entity Category]) <- liftIO $ getCategories $ toSqlKey mbid
      case budget of 
        Nothing -> json $ object [ "message" .= ("No budget was found!" :: String) ]
        Just b -> do
          json $ object [
              "categoryExpenditures" .= map (\x -> object ["spent" .= liftIO $ 
                calculateCategoryBalanceForMonth (entityKey x) 1 >>= (\y -> y) ]) categories
            ]

Up until this point, calculateCategoryBalanceForMonth has type calculateCategoryBalanceForMonth :: CategoryId -> Int -> IO Double.

I am attempting to map over categories and return a list of categoryExpenditures as a JSON object in Aeson. However, I cannot figure out how to return the value from liftIO to the .= operator.

My attempt involved desugaring the assignment operator to >>= (\y -> y) or >>= id where I was hoping id would return a Double, or at least something that had an instance of ToJSON. This does not appear to be the case though.

I also know that using the assignment operator for the liftIO expression, and then passing the assigned variable into the .= operator works, however I was unsure how to use the assignment operator within an anonymous function for map.

What might I be doing wrong? Is any more clarity needed?

Upvotes: 0

Views: 59

Answers (1)

Narvius
Narvius

Reputation: 180

I'm assuming you're working with a monad stack with IO at the bottom.

object takes [Pair] as argument, however you're trying to pass it an IO. (>>=) keeps things inside the monad (by definition!), so (>>= id) does not do what you want it to (in fact, the signature of (>>= id) is Monad m => m (m a) -> m a, ergo join). You want to move things around a bit:

Just b -> do
      expenditures <- forM categories (\x -> do
          spent <- liftIO $ calculateCategoryBalanceForMonth (entityKey x) 1
          return $ object [ "spent" .= spent ])
      json $ object [ "categoryExpenditures" .= expenditures ]

For mapping with a monadic result, use mapM, or forM (forM = flip mapM).

Upvotes: 2

Related Questions