"inferred type arguments do not conform to method's type parameter bounds"

Given the following function:

def nonEmpty[T, A <: IterableOnce[T]](iterableOnce: A, message: String = ""): A =
  if (iterableOnce.iterator.nonEmpty)
    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 IterableOnce.


The 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 =
  if (iterableOnce.iterator.nonEmpty)
    throw new IllegalArgumentException(message)
1 Like

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] = ...
1 Like

That won’t work for types like BitSet

However, using type bounds also doesn’t admit things like Array or String

For properly abstracting over any type that behaves like a collection one needs to use the Is* typeclasses.

import scala.collection.generic.IsIterableOnce
import scala.collection.immutable.BitSet

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:

  1. Traditional C[x] looking types that inherit from IterableOnce[x], like List[x] or Set[x]
  2. Types with different shapes that also inherit from IterableOnce[x], like Map[k, v] or BitSet
  3. Types that actually do not inherit fromIterableOnce[x] but that behave like collections, like Array[x] or String

For 1 & 2, we conclude that we actually need a proper type C rather than a type constructor C[x]
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 C
col: Cit accepts a value of type C
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 IterableOnce
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 List[Int] then A = Int if we pass a BitSet then A = Int if we pass a String then 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.

1 Like

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.


1 Like

Thank you for your explanations, I think I now can gather how the mechanism works.

1 Like