Most of the time, the Scala compiler is able to infer the generic type of some object or function for you. However, when it is unable to do so, it requires you to specify all of the generic arguments (since it will resolve the unknown generics to
Nothing, which I think is still fine).
The problem is when you want to avoid writing out possibly long types, for instance suppose we have the following and a function.
class ParsecE[S <: Stream[_, _], U, E, +A](/*args*/) def pure[S <: Stream[_, _], U, E, A](a: =>A): ParsecE[S, U, E, A]
Now suppose we’ve got this function call somewhere
pure[S, U, E, ParsecE[S, U, E, A]](atom). This is a bit long for my liking… for
pure(atom) the compile infers
ParsecE[Nothing, Nothing, Nothing, ParsecE[S, U, E, A]] so we are forced to add those generics. But the compiler got the last parameter correct! What would be nice is if we could write something like this instead;
pure[S=S, U=U, E=E](atom).
An additional way this problem could be solved is by implicit generics; This requires a bit more work to implement however. Let’s redefine
pure like so
class ParsecE[+A][implicit S <: Stream[_, _], U, E](/*args*/) def pure[A][implicit S <: Stream[_, _], U, E](a: =>A): ParsecE[A] //S, U, E is implicit for ParsecE
Then, if we have the implicit types of
S, U, E defined somewhere, then the compiler can substitute them in, allowing us to write
So where would the compiler look for these implicit generics? Well the first option would be if the enclosing class or method had generics of the same name defined and they were implicit (you can see this in the new definition of
pure in the return value). For example, the
pure(atom) call is within a method of the class
ExpressionParser[S <: Stream[_, _], U, E, A]. If this were defined as
ExpressionParser[A][implicit S <: Stream[_, _], U, E] then that provides all three types and they fit the constraints.
Alternatively we could declare implicit types in the same scope as the
pure(atom) call, like so;
implicit type S = Stream[String, Char] implicit type U = Unit implicit type E = Default pure(atom) // A is inferable, that's no problem, then there is an implicit S, U, E in scope! pure[ParsecE[A]](atom) // also valid, the S, U, E are implicitly available for both types!