Indeed the function parameter T
is different from type parameter T
, for example
def f[T](T: T): T = T
f(42) // res0: Int = 42
Compiler does not get confused because values exist in a different universe from types:
...there exist two separate universes, the universe of types and the
universe of values. In the universe of values, we have methods which
take values as arguments in round parentheses (or occasionally curly
braces). In the universe of types, we have type constructors, which take types
as arguments in square brackets.
This convention is sometimes used when dealing with typeclasses. It is meant to communicate that we want to simply return the typeclass instance resolved for F
. To avoid confusion you could use ev
approach you already suggested in question, or even
object Program {
def apply[F[_]: Program]: Program[F] = implicitly[Program[F]]
}
As a side-note, this trick with apply
method in companion object of typeclass allows us to avoid having to use implicitly
, for example, given
trait Foo[T]
trait Bar[T]
trait Program[F[_]]
implicit val fooProgram: Program[Foo] = ???
implicit val barProgram: Program[Bar] = ???
object Program {
def apply[F[_]: Program]: Program[F] = implicitly
}
then we can write
Program[Bar]
instead of
implicitly[Program[Bar]]