Given the following function:
def nonEmpty[T, A <: IterableOnce[T]](iterableOnce: A, message: String = ""): A =
throw new IllegalArgumentException(StringUtils.trimToNull(message))
When I try to pass a list of Strings:
def foo(list: List[String]): Unit = nonEmpty(list)
I get the following error: inferred type arguments [Nothing,List[String]] do not conform to method nonEmpty’s type parameter bounds [T,A <: IterableOnce[T]]
I have trouble to understand why I get error and how I need to modify
nonBlank to make it generic for all subtypes fof
T type parameter is not linked to any argument of your method. You just require that there is some
T and that
A is some IterableOnce of
T. So the compiler happily infers the bottom type for
T as that’s a subtype for all types.
def nonEmpty[A <: IterableOnce[_]](iterableOnce: A, message: String = ""): A =
throw new IllegalArgumentException(message)
Here’s another way to express it (which I prefer, because it retains the shapes of the type constructors).
def nonEmpty[F[a] <: IterableOnce[a], A](iterableOnce: F[A], message: String = ""): F[A] = ...
That won’t work for types like
However, using type bounds also doesn’t admit things like
For properly abstracting over any type that behaves like a collection one needs to use the
def acceptsAnyIterable[C](col: C)(implicit ev: IsIterableOnce[C]): Option[ev.A] =
You can see the code running here.
This actually works exactly as I need it. But I don’t understand why. What’s the Scasla-magic behind that?
Uhm is hard to explain without knowing what exactly is confusing.
I will try to go step by step, I hope I cover everything without being too tedious.
If I miss something, please let me know so I could try to explain that in more detail.
First, let’s see the goal:
“Be able to accept any collection-like value”.
Then let’s see what that means:
C[x] looking types that inherit from
- Types with different shapes that also inherit from
Map[k, v] or
- Types that actually do not inherit from
IterableOnce[x] but that behave like collections, like
For 1 & 2, we conclude that we actually need a proper type
C rather than a type constructor
And for 3, we conclude that we need a typeclass rather than subtyping.
Now, the question would be:
“Does such typeclass already exists in the stdlib?”
The answer is yes, it is
IsIterableOnce; from the name and the Scaladoc we can conclude that its presence means that the type
C can be view as an
IterablceOnce. AS we can see it only provides an
apply method that will turn our
C into an
IterableOnce, it also has a path-dependant type
A which represents the type of the elements inside the collection.
acceptsAnyIterable[C] means the method is generic on
col: Cit accepts a value of type
implicit ev: IsIterableOnce[C] we require the presence of
IsIterable for that type
C; or in other words, we require that such type behaves as an
Option[ev.A] the return type is an
Option of the elements inside the collection, such type is dependent on the evidence, and in turn, is dependant on the provided value. If we pass a
A = Int if we pass a
A = Int if we pass a
A = Char.
ev(col).iterator using the evidence we turn the
col into an
IterableOnce[A] and then we get an
Iterator[A] from it.
Thank you so much! I think I need to learn more about the concept of typeclasses.
I have another question: Who’s supplying the implicit value?
Short answer, the compiler.
Thank you for your explanations, I think I now can gather how the mechanism works.