1

Going through Play Form source code right now and encountered this

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = {

I am guessing it takes request as an implicit parameter (you don't have to call bindFromRequet(request)) of type play.api.mvc.Request[_] and return a generic T type wrapped in Form class. But what does [_] mean.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Kevin
  • 2,187
  • 4
  • 26
  • 35

2 Answers2

1

The method doesn't take a play.api.mvc.Request, it takes a play.api.mvc.Request parameterized with another type. You could give the type parameter a name:

def bindFromRequest()(implicit request: play.api.mvc.Request[TypeParameter]): Form[T] = {

But since you're not referring to TypeParameter anywhere else it's conventional to use an underscore instead. I believe the underscore is special cased as a 'black hole' here, rather than being a regular name that you could refer to as _ elsewhere in the type signature.

Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157
1

The notation Foo[_] is a short hand for an existential type:

Foo[A] forSome {type A}

So it differs from a normal type parameter by being existentially quantified. There has to be some type so your code type checks where as if you would use a type parameter for the method or trait it would have to type check for every type A.

For example this is fine:

val list = List("asd");

def doSomething() {
    val l: List[_] = list
}

This would not typecheck:

def doSomething[A]() {
    val l: List[A] = list
}

So existential types are useful in situations where you get some parameterized type from somewhere but you do not know and care about the parameter (or only about some bounds of it etc.)

In general, you should avoid existential types though, because they get complicated fast. A lot of instances (especially the uses known from Java (called wildcard types there)) can be avoided be using variance annotations when designing your class hierarchy.

dth
  • 2,287
  • 10
  • 17