Domanda

The types of symbols class A[_] or of def a[_](x: Any) have a type parameter that can't be referenced in the body, thus I don't see where it is useful for and why it compiles. If one tries to reference this type parameter, an error is thrown:

scala> class A[_] { type X = _ }
<console>:1: error: unbound wildcard type
       class A[_] { type X = _ }
                             ^

scala> def a[_](x: Any) { type X = _ }
<console>:1: error: unbound wildcard type
       def a[_](x: Any) { type X = _ }
                                   ^

Can someone tell me if such a type has a use case in Scala? To be exact, I do not mean existential types or higher kinded types in type parameters, only those litte [_] which form the complete type parameter list.

È stato utile?

Soluzione

Because I did not get the answers I expected, I brought this to scala-language.

I paste here the answer from Lars Hupel (so, all credits apply to him), which mostly explains what I wanted to know:

I'm going to give it a stab here. I think the use of the feature gets clear when talking about type members.

Assume that you have to implement the following trait:

trait Function {
  type Out[In]
  def apply[In](x: In): Out[In]
}

This would be a (generic) function where the return type depends on the input type. One example for an instance:

val someify = new Function {
  type Out[In] = Option[In]   def
  apply[In](x: In) = Some(x)
}

someify(3) res0: Some[Int] = Some(3)

So far, so good. Now, how would you define a constant function?

val const0 = new Function {
  type Out[In] = Int
  def apply[In](x: In) = 0
}

const0(3) res1: const0.Out[Int] = 0

(The type const0.Out[Int] is equivalent to Int, but it isn't printed that way.)

Note how the type parameter In isn't actually used. So, here's how you could write it with _:

val const0 = new Function {
  type Out[_] = Int
  def apply[In](x: In) = 0
}

Think of _ in that case as a name for the type parameter which cannot actually be referred to. It's a for a function on the type level which doesn't care about the parameter, just like on value level:

(_: Int) => 3 res4: Int => Int = <function1>

Except …

type Foo[_, _] = Int
<console>:7: error: _ is already defined as type _
       type Foo[_, _] = Int

Compare that with:

(_: Int, _: String) => 3 res6: (Int, String) => Int = <function2>

So, in conclusion:

type F[_] = ConstType // when you have to implement a type member def
foo[_](...) // when you have to implement a generic method but don't
            // actually refer to the type parameter (occurs very rarely)

The main thing you mentioned, class A[_], is completely symmetric to that, except that there's no real use case.

Consider this:

trait FlyingDog[F[_]] { def swoosh[A, B](f: A => B, a: F[A]): F[B] }

Now assume you want to make an instance of FlyingDog for your plain old class A.

new FlyingDog[A] { ... }
// error: A takes no type parameters, expected: one
// (aka 'kind mismatch')

There are two solutions:

  1. Declare class A[_] instead. (Don't do that.)

  2. Use a type lambda:

    new FlyingDog[({ type λ[α] = A })#λ]
    

or even

new FlyingDog[({ type λ[_] = A })#λ]

Altri suggerimenti

I had some casual ideas about what it could mean here:

https://issues.scala-lang.org/browse/SI-5606

Besides the trivial use case, asking the compiler to make up a name because I really don't care (though maybe I'll name it later when I implement the class), this one still strikes me as useful:

Another use case is where a type param is deprecated because improvements in type inference make it superfluous.

trait T[@deprecated("I'm free","2.11") _, B <: S[_]] 

Then, hypothetically, one could warn on usage of T[X, Y] but not T[_, Y].

Though it's not obvious whether the annotation would come before (value parameter-style) or after (annotation on type style).

[Edit: "why it compiles": case class Foo[_](i: Int) still crashes nicely on 2.9.2]

The underscore in Scala indicates an existential type, i.e. an unknown type parameter, which has two main usage:

  • It is used for methods which do not care about the type parameter
  • It is used for methods where you want to express that one type parameter is a type constructor.

A type constructor is basically something that needs a type parameter to construct a concrete type. For example you can take the following signature.

def strangeStuff[CC[_], B, A](b:B, f: B=>A): CC[A] 

This is a function that for some CC[_] , for example a List[_], creates a List[A] starting from a B and a function B=>A.

Why would that be useful? Well it turns out that if you use that mechanism together with implicits and typeclasses, you can get what is called ad-hoc polymorphism thanks to the compiler reasoning.

Imagine for example you have some higher-kinded type: Container[_] with a hierarchy of concrete implementations: BeautifulContainer[_], BigContainer[_], SmallContainer[_]. To build a container you need a

trait ContainerBuilder[A[_]<:Container[_],B] { 

def build(b:B):A[B]

}

So basically a ContainerBuilder is something that for a specific type of container A[_] can build an A[B] using a B.

While would that be useful ? Well you can imagine that you might have a function defined somewhere else like the following:

def myMethod(b:B)(implicit containerBuilder:ContainerBuilder[A[_],B]):A[B] = containerBuilder.build(b)

And then in your code you might do:

val b = new B()
val bigContainer:BigContainer[B] = myMethod(b)
val beautifulContainer:BeautifulContainer[B] = myMethod(b)

In fact, the compiler will use the required return type of myMethod to look for an implicit which satisfies the required type constraints and will throw a compile error if there is no ContainerBuilder which meets the required constraints available implicitely.

That's useful when you deal with instances of parametrized types without caring of the type parameter.

trait Something[A] {
  def stringify: String
}

class Foo extends Something[Bar] {
  def stringify = "hop"
}

object App {
  def useSomething(thing: Something[_]) :String = {
    thing.stringify
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top