silkapp / rest

Packages for defining APIs, running them, generating client code and documentation.
http://silkapp.github.io/rest
390 stars 52 forks source link

rest-example - example of handling nested ReaderT #144

Closed ronslow closed 8 years ago

ronslow commented 8 years ago

I am following rest-examples, simplifying, and building a service to return a single Booking, identified by BookingId, from an (AcidState Diary)

newtype DiaryApi a = DiaryApi { unDiaryApi :: ReaderT (AcidState Diary) IO a }
  deriving ( Applicative
           , Functor
           , Monad
          , MonadIO
          , MonadReader (AcidState Diary)

       )

type WithBookingId = ReaderT BookingId DiaryApi

resource ::  Resource DiaryApi WithBookingId BookingId Void Void
resource = mkResourceReader { R.name = "booking"

                        , R.get = Just getBooking
                        }

getBooking ::  Handler WithBookingId
getBooking = mkIdHandler jsonO handler where
 handler :: () -> BookingId -> ExceptT (Reason Void) WithBookingId  Booking
 handler _ bid = do
   diary <- lift $ DiaryApi $ lift $ ask -- this is the problem line
   return $ liftIO $ query diary (GetBooking bid) -- this line is looking up the booking in the       AcidState Diary, works within IO, and is known to be correct

The problem line of course lifts the diary into a ExceptT (Reason Void) BookingApi Booking monad, whereas I need it to be inside an ExceptT (Reason Void) WithBookingId Booking monad

Am I going about this in the right way? Is it correct to nest ReaderT in this way for subresources?

Robert

ronslow commented 8 years ago
getBooking = mkIdHandler jsonO handler where
  handler :: () -> BookingId -> ExceptT (Reason Void) WithBookingId  (Maybe Booking)
  handler _ bid = lift handler'
  handler' :: WithBookingId (Maybe Booking)
  handler' = do
    bid <- ask
    lift $ handler'' bid
  handler'' :: BookingId -> DiaryApi (Maybe Booking)
  handler'' bid = do
      diary <- DiaryApi ask
      liftIO $ query diary (AS.GetBooking bid)

The moral of the story was of course to subdivide the problem into typechecked definitions and then use the typechecker to guide to the solution

hesselink commented 8 years ago

Sorry for not responding sooner to this, good to see you found the solution (lift ask).