As you and others have already noticed, toSet
doesn't provide a parameter list. Thus, calling it with parentheses, will always result in a compilation error unless the compiler finds an apply method that expects an argument as it is the case in your example:
scala> List(1).toSet()
res2: Boolean = false
scala> List(1).toSet.apply()
res3: Boolean = false
scalac has a feature called "adapting argument lists", which is visible with -Xlint
:
scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
signature: GenSetLike.apply(elem: A): Boolean
given arguments: <none>
after adaptation: GenSetLike((): Unit)
List(1).toSet()
^
res7: Boolean = false
scalac tries to wrap the argument into a tuple, as one can see in the sources (where an empty argument list will be treated as the Unit
literal by gen.mkTuple
):
/* Try packing all arguments into a Tuple and apply `fun`
* to that. This is the last thing which is tried (after
* default arguments)
*/
def tryTupleApply: Tree = (
if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) {
val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args)))
// expected one argument, but got 0 or >1 ==> try applying to tuple
// the inner "doTypedApply" does "extractUndetparams" => restore when it fails
val savedUndetparams = context.undetparams
silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t =>
// Depending on user options, may warn or error here if
// a Unit or tuple was inserted.
val keepTree = (
!mode.typingExprNotFun
|| t.symbol == null
|| checkValidAdaptation(t, args)
)
if (keepTree) t else EmptyTree
} orElse { _ => context.undetparams = savedUndetparams ; EmptyTree }
}
else EmptyTree
)
Which btw, is a feature that is not mentioned in the spec. Adding the parentheses explicitly will let the warning go away:
scala> List(1).toSet(())
res8: Boolean = false
One remaining question now is why the code above does not produce a compilation error due to the fact that the list is of type List[Int]
and the apply method of Set
has type signature apply(A): Boolean
, thus expects an Int
in our case. The reason for this is a very well known problem and a result of the type signature of toSet
which is toSet[B >: A]: Set[B]
. The type signature denotes a lower bound, which means that any supertype of Int
can be passed as argument.
Because in our case Unit
is specified as the type of the argument, the compiler has to search for a common supertype of Unit
and Int
that matches the type signature of toSet
. And because there is such a type, namely AnyVal
, the compiler will infer that type and going forward without falling apart with an error:
scala> List(1).toSet[AnyVal](())
res9: Boolean = false