Below is a specific instance of a kind of situation that I sometimes encounter with parameterized types. Basically, there are type parameters that I know are compatible, but I don't know how to prove that to some parts of the code.
I'm writing a request router that maps urls to handler functions. Below is some simplified code. I create a List[Route]
where a Route
is basically a UrlMatcher, Function
pair.
class Route[A](matcher: UrlMatcher[A], handler: HandlerFunction[A])
abstract class UrlMatcher[A] {
def match(url: String): Option[A] // None if no match
The type parameter A
is for the "arguments" that the matcher might extract from the URL. They would be passed to the handler function. For example, a UrlMatcher[Int]
that sees a URL path like "/users/123" could pass 123 to a getUser(id: Int)
function. The router might look like:
val routes = ArrayBuffer[Route[_]]
def callHandler(url: String) {
for (r <- routes) {
val args = r.matcher.matchUrl(url)
if (args.isDefined)
r.handler(args.get) // <--- error here
}
The problem is that I get type mismatch errors because I don't know how to tell it the two types are the same.
type mismatch; found: args.type (with underlying type Option[Any])
required: _$1
I know I can redesign it so that Route
has a method like matchAndCall
, but I'd like to keep this logical flow if possible.
Update/Edit
I don't fully understand existential types, but I tried this...
val routes = ArrayBuffer[T forSome { type T }]()
And it removed the mismatch error above. However, I have another one where I was inserting into the ArrayBuffer
.
def route[P](matcher: UrlMatcher[P], handler: Handler[P]): AbstractRoute = {
val route = new Route(matcher, handler)
otherRoutes.append(route) // error here
route
}
Now the error is...
type mismatch; found : Route[P] required: Route[T forSome { type T }] Note: P <: T
forSome { type T }, but class Route is invariant in type P. You may wish to define
P as +P instead. (SLS 4.5)
Why is P
incompatible with T
, since their are no constraints on T
?