Alternatives to Generic Closures?

I'm working on a new feature for Alamofire that allows users to capture a variety of events. Some of these events happen in generic contexts, and I'd like to pass those generics along to the events. This works fine with the protocol requirements I've created:

protocol RequestEventMonitor {
    // ...
    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value>)
}

However, one of the prebuilt monitors I want to provide is one that allows the user to just set closures to be called when events happen. However, it's long been a restriction that you can't define generic closures without providing a type from a higher scope like a generic type. This isn't really tenable for my ClosureEventMonitor. I suppose I could offer an API like:

var requestDidParseResponse: ((DataRequest, DataResponse<Any>) -> Void)?

But I really don't want to lose the full fidelity of the generic. Any other ideas that are as simple to use as setting a closure?

What does the generic get you here? There are no constraints so implementing code wouldn’t be able to do anything with a generic that it couldn’t with Any.

This is designed to be a flexible API for anyone who needs to monitor events from Alamofire, so right now the generic is mainly there to maintain fidelity in case anyone comes up with a specific use for it. I realize users could cast the Any to check types for something, but it seems better for the library to provide it automatically. And aside from the closure API, providing it hasn't cost me anything yet.

I see two possible solutions here.

  1. Use a type eraser like AnyDataResponse instead of DataResponse<Any> (is easier for registering callbacks in your situation)
  2. Use DataResponse<Any> but then extend DataResponse with:
func traverse<T>(with transform: (Value) throws -> T?) rethrows -> DataResponse<T>?

In last case you can do something like this:

requestDidParseResponse = { 
  if let response = $1.traverse(with: { $0 as? TypeWeNeed }) {
    otherMonitor.request($0, didParseResponse: response)
  }
}