Prevent 0-ary functions from being called implicitly in Scala - scala

I got nipped by a production bug where I passed an impure 0-ary function to a class that mistakenly expected a a bare result type.
def impureFunc(): Future[Any] = ???
case class MyService(impureDependency: Future[Any] /* should have been () => Future[Any] */)
Effectively, this made MyService immediately invoke impureFunc and cache the first result for the lifetime of the program, which led to a very subtle bug.
Normally, the type system prevents these sort of bugs, but because of the ability to call 0-ary functions without an argument list, the compiler accepted this program.
Obviously, this is a "feature" of Scala, designed to make code look cleaner, but this was a bad gotcha. Is there any way to make this a compiler warning or a linting error? In other words, disapprove the "Empty application" type of implicit method conversion?

From the comments here, it appears this behavior was deprecated with a warning in 2.12 and should become an error in 2.13. So it seems the answer is to use -deprecation -Xfatal-warnings after upgrading.

Related

How to configure `sbt` to not display warnings "Warning private default argument in object * is never used"

I have a scala compiler 2.12.11 and compiler prints some warnings, like:
private default argument in class SomeClass is never used
while as it is used.
I have seen a Scala 2.12.2 emits a ton of useless "Warning: parameter value ... in method ... is never used" warnings. How to get rid of them? however it doesn't help me, as it is not possible to negate params.
Can you help me to surpress this warning, however saving another unused warnings? Currently, I have -Xlint option.
Instead of -Xlint which is a batch, you can turn on specific options manually. For instant in 2.13 I can print available options like this:
scalac -Xlint:help
Enable recommended warnings
adapted-args An argument list was modified to match the receiver.
nullary-unit `def f: Unit` looks like an accessor; add parens to look side-effecting.
inaccessible Warn about inaccessible types in method signatures.
infer-any A type argument was inferred as Any.
missing-interpolator A string literal appears to be missing an interpolator id.
doc-detached When running scaladoc, warn if a doc comment is discarded.
private-shadow A private field (or class parameter) shadows a superclass field.
type-parameter-shadow A local type parameter shadows a type already in scope.
poly-implicit-overload Parameterized overloaded implicit methods are not visible as view bounds.
option-implicit Option.apply used an implicit view.
delayedinit-select Selecting member of DelayedInit.
package-object-classes Class or object defined in package object.
stars-align In a pattern, a sequence wildcard `_*` should match all of a repeated parameter.
strict-unsealed-patmat Pattern match on an unsealed class without a catch-all.
constant Evaluation of a constant arithmetic expression resulted in an error.
unused Enable -Wunused:imports,privates,locals,implicits,nowarn.
nonlocal-return A return statement used an exception for flow control.
implicit-not-found Check #implicitNotFound and #implicitAmbiguous messages.
serial #SerialVersionUID on traits and non-serializable classes.
valpattern Enable pattern checks in val definitions.
eta-zero Usage `f` of parameterless `def f()` resulted in eta-expansion, not empty application `f()`.
eta-sam The Java-defined target interface for eta-expansion was not annotated #FunctionalInterface.
deprecation Enable -deprecation and also check #deprecated annotations.
byname-implicit Block adapted by implicit with by-name parameter.
recurse-with-default Recursive call used default argument.
unit-special Warn for specialization of Unit in parameter position.
multiarg-infix Infix operator was defined or used with multiarg operand.
implicit-recursion Implicit resolves to an enclosing definition.
Default: All choices are enabled by default.
so I could e.g. enable -Xlint:inaccessible -Xlint:adapted-args -Wunused:privates,locales or whatever I want instead of everything. For 2.12 this list would be different. (You can also check scalac -X, scalac -Y and scalac -W).
An alternative is to enable warnings and suppress them when you "breaking" something consciously. For unused you have #scala.annotation.unused, for other warnings in 2.12 and before there is silencer plugin and since 2.13 there is #scala.annotation.nowarn annotation.

Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want [duplicate]

I'm just in the process of upgrading from Scala 2.10.x to 2.11.2 and I'm receiving the following warning with the following code:
override def validateKey(key: String): Either[InvalidKeyError, Unit] =
keys.contains(key) match {
case true => Right()
case false => Left(InvalidKeyError(context, key))
}
Adaptation of argument list by inserting () has been deprecated: this
is unlikely to be what you want. signature: Right.apply[A, B](b: B):
scala.util.Right[A,B] given arguments: after adaptation:
Right((): Unit)
I am able to solve this by changing the "true" case statement to:
case true => Right(()) //() is a shortcut to a Unit instance
Is this the proper way to address this warning?
Edit: perhaps a "why we have to do this now" type answer would be appropriate, my cursory investigation seems to indicate that Scala inserting "Unit" when it thinks it needs to causes other problems
Automatic Unit inference has been deprecated in scala 2.11, and the reason behind this is that it can lead to confusing behavior, especially for people learning the language.
Here's an example
class Foo[T](value: T)
val x = new Foo
This should not compile, right? You are calling the constructor with no arguments, where one is required. Surprisingly, until scala 2.10.4 this compiles just fine, with no errors or warnings.
And that's because the compiler inferred a Unit argument, so it actually replaced your code with
val x = new Foo[Unit](()) // Foo[Unit]
As the newly introduced warning message says, this is unlikely to be what you want.
Another famous example is this
scala> List(1,2,3).toSet()
// res1: Boolean = false
calling toSet() should be a compile-time error, since toSet does not take arguments, but the compiler desperately tries to make it compile, ultimately interpreting the code as
scala> List(1,2,3).toSet.apply(())
which means: test whether () belongs to the set. Since it's not the case, you get a false!
So, starting from scala 2.11, you have to be explicit if you want to pass () (aka Unit) as an argument. That's why you have to write:
Right(())
instead of
Right()
examples taken from Simplifying Scala — The Past, Present and Future by Simon Ochsenreither.
Perhaps it should be Right(()). Have you tried that?
My explanation is that since the Right.apply is polymorphic it can take all kind of parameters, doing Right() means passing in a Unit and the compiler simply advise you that maybe that's not what you want, he doesn't know that this is what you actually want.
If you see your deprecate message it states:
... after adaptation: Right((): Unit)
Which means that the compiler has automatically decided that you are passing in a Unit, since this is kinda like void he doesn't really likes it, specifically passing in a Unit like () explicitly tells the compiler that you do want a Unit there. Anyway seems a new deprecation form scala 2.11, I can't reproduce this on 2.10.4.

REPL warning on using structural types

Why REPL gives warning on using structural types? Are structural types unsafe to use?
scala> def test(st: { def close():Unit})
| = st.close()
<console>:12: warning: reflective access of structural type member method close should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scaladoc for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
= st.close()
^
test: (st: AnyRef{def close(): Unit})Unit
The warning suggests viewing the Scaladoc page, which says
Why control it? Reflection is not available on all platforms. Popular
tools such as ProGuard have problems dealing with it. Even where
reflection is available, reflective dispatch can lead to surprising
performance degradations.
Source
To make the warning go away, simply add import scala.language.reflectiveCalls to the top of your file to indicate that, yes, you do in fact intend to use this language feature.
I would like to put another point of view than Silvio Mayolo's answer.
Fundamentally the important thing here is that Scala is compiled into a JVM rather than some Scala-specific target platform. This means that many features that exist in Scala are not supported out of the box by the target platform (JVM) and they have to be somehow simulated by the compiler. One of such features is structural types and it is implemented using reflection. And using reflection introduces a bunch of potential issues that are mentioned in Silvio's answer. Thus compiler developers want to make sure that you understand possible drawbacks and still want to use this feature by enforcing either explicit import, compiler configuration or warning.
As for alternatives in your case you may use java.lang.AutoCloseable instead of your structural type and it will probably cover most of your cases.
Another alternative is to use implicit parameter and type class idea:
trait CloseableTC[A] {
def close(closeable: A): Unit
}
object CloseableTC {
implicit val javaCloseable: CloseableTC[java.lang.AutoCloseable] = new CloseableTC[java.lang.AutoCloseable] {
override def close(closeable: AutoCloseable): Unit = closeable.close()
}
// add here more implicits for other classes with .close()
}
def testTC[A](a: A)(implicit closeableTC: CloseableTC[A]) = closeableTC.close(a)
import CloseableTC._

Adaptation of argument list by inserting () has been deprecated

I'm just in the process of upgrading from Scala 2.10.x to 2.11.2 and I'm receiving the following warning with the following code:
override def validateKey(key: String): Either[InvalidKeyError, Unit] =
keys.contains(key) match {
case true => Right()
case false => Left(InvalidKeyError(context, key))
}
Adaptation of argument list by inserting () has been deprecated: this
is unlikely to be what you want. signature: Right.apply[A, B](b: B):
scala.util.Right[A,B] given arguments: after adaptation:
Right((): Unit)
I am able to solve this by changing the "true" case statement to:
case true => Right(()) //() is a shortcut to a Unit instance
Is this the proper way to address this warning?
Edit: perhaps a "why we have to do this now" type answer would be appropriate, my cursory investigation seems to indicate that Scala inserting "Unit" when it thinks it needs to causes other problems
Automatic Unit inference has been deprecated in scala 2.11, and the reason behind this is that it can lead to confusing behavior, especially for people learning the language.
Here's an example
class Foo[T](value: T)
val x = new Foo
This should not compile, right? You are calling the constructor with no arguments, where one is required. Surprisingly, until scala 2.10.4 this compiles just fine, with no errors or warnings.
And that's because the compiler inferred a Unit argument, so it actually replaced your code with
val x = new Foo[Unit](()) // Foo[Unit]
As the newly introduced warning message says, this is unlikely to be what you want.
Another famous example is this
scala> List(1,2,3).toSet()
// res1: Boolean = false
calling toSet() should be a compile-time error, since toSet does not take arguments, but the compiler desperately tries to make it compile, ultimately interpreting the code as
scala> List(1,2,3).toSet.apply(())
which means: test whether () belongs to the set. Since it's not the case, you get a false!
So, starting from scala 2.11, you have to be explicit if you want to pass () (aka Unit) as an argument. That's why you have to write:
Right(())
instead of
Right()
examples taken from Simplifying Scala — The Past, Present and Future by Simon Ochsenreither.
Perhaps it should be Right(()). Have you tried that?
My explanation is that since the Right.apply is polymorphic it can take all kind of parameters, doing Right() means passing in a Unit and the compiler simply advise you that maybe that's not what you want, he doesn't know that this is what you actually want.
If you see your deprecate message it states:
... after adaptation: Right((): Unit)
Which means that the compiler has automatically decided that you are passing in a Unit, since this is kinda like void he doesn't really likes it, specifically passing in a Unit like () explicitly tells the compiler that you do want a Unit there. Anyway seems a new deprecation form scala 2.11, I can't reproduce this on 2.10.4.

Requesting more information about #inline from the compiler?

The documentation for #inline states:
An annotation on methods that requests that the compiler should try especially hard to inline the annotated method.
However, unlike the similar #tailrec annotation, the compiler does not (by default) offer any information about whether or not it managed to inline a method.
Is there any way to determine if the compiler managed to inline an annotated method?
Specifically, I'd like for the compiler to tell me, for example, that in all reasonable cases it will be able to inline a method I marked. (Some situtations I can think of where it would warn me that it cannot inline a method is if it is not final, and hence requires a vtable lookup if the class is subclassed)
Related questions:
When should I (and should I not) use Scala's #inline annotation?
Does the #inline annotation in Scala really help performance?
First, you need to remember that Scalac will only try to inline things when you compile with -optimise (or -Yinline I think).
Consider the following simple case:
class Meep {
#inline def f(x: Int) = x + 19
}
object Main extends App {
new Meep().f(23)
}
If I compile that with -optimise, Scalac will gives me a warning: there were 1 inliner warnings; re-run with -Yinline-warnings for details. Now, apart from the grammar giggle, this didn't give me much.
So let's recompile with -Yinline-warnings. Now I get: At the end of the day, could not inline #inline-marked method f. Uh, OK, that was not very helpful either, but I guess that's what I get for using a private compiler flag. :) Some of the inline warnings are a tiny bit more helpful, by the way - like: Could not inline required method f because bytecode unavailable. (which happens in the REPL)
The compiler help explains -Yinline-warnings as Emit inlining warnings. (Normally surpressed due to high volume), so I guess it has to be used on a case-by-case basis.
Anyway, if we change the definition of f in the above snippet to #inline final def f(x: Int) = x + 19, the inline warning will go away and the method will be properly inlined.
Hope that helped a bit.