Correct abstraction for event handlers that return values

Hi everyone, I’m in the process of updating a fork of purescript-electron to the newest compiler/electron versions. I’m trying to port the IpcMain.handle function (docs), which I import from FFI as:

foreign import data Handler :: Type

foreign import handle :: String -> Handler -> Effect Unit

foreign import _mkHandler :: (forall a. Aff a → Effect (Promise a)) -> (IpcMainInvokeEvent -> Json -> Aff Json) -> Handler

mkHandler :: (IpcMainInvokeEvent -> Json -> Aff Json) -> Handler
mkHandler handler = _mkHandler fromAff handler

This works, but, as I was writing some usage examples, I stumbled upon a problem: what would be the simplest way to use the handler outside of Aff?

onIpcMessage :: IpcMainInvokeEvent -> Json -> Aff Json -- This works
onIpcMessage' :: IpcMainInvokeEvent -> Json -> AppM Json -- ???

I turned to the trusty coroutines, but of course their emit returns an Unit:

mkHandler \e v -> do
    CRA.emit emitter "This goes to the consumer"
    pure $ fromString "This gets sent to the IPC"

Which abstraction should I use for event handlers that return a value?

I should add that in the meantime I am using this workaround:

IPC.mkHandler \e v -> do
    deferred <- liftEffect $ deferPromise
--                     ┌──── Emit a "suspended Aff" and the IPC arguments
    CRA.emit emitter (tuple3 deferred e v)  
    deferToAff toAff deferred

Where i defined a (probably incorrect?) way of suspending promises:

foreign import data Deferred :: Type -> Type

foreign import deferPromise :: forall a. Effect (Deferred a)

--                                        ┌──── The type of toAff
foreign import deferToAff :: forall a. (forall p. Promise p → Aff p) -> Deferred a -> Aff a

foreign import resolveDeferred :: forall a. Deferred a -> a -> Effect Unit

And then in my handler (again, inside a generic AppM) I can:

--                                    ┌──── The tuple from mkHandler
liftEffect $ resolveDeferred (get1 deferred) "EXAMPLE_VALUE"

But im pretty sure it only works by chance, doesn’t it?