--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Intuitively, I think it would make more sense to annotate methods (and constructors) @pure
rather than classes. In your example, how do you know that the printHello
method does not perform any side-effects?
Intuitively, I think it would make more sense to annotate methods (and constructors)
@pure
rather than classes. In your example, how do you know that theprintHello
method does not perform any side-effects?
OK. But note that is not how the existing warning works: it really checks purity
of the expression that is discarded. Adapting your example to a built-in type:
scala> def printHello = 3
printHello: Int
scala> def blah(): Unit = printHello
blah: ()Unit
There’s no warning here.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
I think there are two different but overlapping concerns. One is forgetting you need to do something with a value. The only time I can think of that it's a big problem is, there's a confusing assumption that an effect should take place, but actually you have to pass the captured effect along for it to run. Another is coercion to Unit (by inserting () thus resulting in value discarding. This can be annoying (why didn't the compiler catch my typo) in much more places, but on its own isn't as catastrophic.
If a method returns a String or JButton or whatever and I call the method and discard it, no big deal. If you realize you need a String or whatever later, you'll figure out what to do. And if you don't feel a need to use a String, no harm done (except for some wasted CPU cycles).
There's a much more specific and catastrophic case, which is a type that captures effects, like IO and all its cousins. Because there, there's lots of grounds for confusion. You might need the value later (so it gets evaluates) and not realize it. You thought you already ran some effect. So that's much more dangerous. And that's pretty strongly tied to the type, not particular methods. For example any method that returns an IO has this potential for confusion.
This is completely unrelated to whether it should be a type mismatch to assign an Int to Unit.
I would call the annotation @noDiscard and make it a compiler error to discard it, actually. For those use cases, there's basically zero reason to ever discard it. And if there might be, the library author (or other) can provide a .discard combinator.
I agree that -Ywarn-value-discard
is probably too noise to be useful in most projects.
However, I don’t think that a type-based approach is correct in this context. As a library
author you can write a class and annotate it @warnIfDiscarded
. However, anybody
using that class may write a side-effecting method that returns a value of this type.
For example, should we warn about discarding an Int
or not? For example:
class Counter {
private var v: Int
def inc(): Int = { v += 1; v }
}
This is a sensible definition: inc
returns the new value. There are situations when a
client wants to use the returned value, but also situations when it makes sense to just
discard it.
So my point is that you cannot decide on the class-author-side if it makes sense to
annotate warnIfDiscarded
or not.
--
Would be nice to annotate the type rather than the method.
def foo: Int with Importance = …
def bar = {
foo
33
}
is bar: Int with Importance
or not?
--Cheers,√
On Tuesday, May 24, 2016 at 2:42:57 AM UTC-7, nafg wrote:
I would call the annotation @noDiscard and make it a compiler error to discard it, actually. For those use cases, there's basically zero reason to ever discard it. And if there might be, the library author (or other) can provide a .discard combinator.
Re-posting with the intended markdown formatting:
I agree with the @noDiscard idea. This is not about purity or termination and many messages here seem to have wandered off into the wilderness.
It does not apply to the ‘Int or Int with Importance’ example. @noDiscard would tag a type that the author knows represents effect capture or lazy execution. User libraries that are IO like would all happily add this if it lead to smart warnings.
I’m not sure if any basic scala type is a candidate for this. Any function type that returns Unit might be a candidate for effect capture, but such an annotation would apply to all FunctionX, not only those with Unit type params.
toy example:
def foo: () => String = () => { println("its happening!"); "blah" }
def bar(s: String): String => Unit = println
def blah(): Unit = {
foo // not what the author intended
}
def blargh(): Unit = {
bar("oops")
}
def nowWhat(): Unit = {
foo
bar("oops")
println("done")
}
The first two above are caught by -Ywarn-value-discard , which I generally use and put up with manually adding ()
but some do not. The third is not, even though we discarded two suspicious values.
The first value is impossible to prove that is has an effect.
The second value is most likely an effect — A unit returning function.
In both cases, a function was created and discarded, which is suspect whether or not there is an effect. Is there a -Ywarn-function-discard ?
Below is a scalaz.concurrent.Task example that has nothing to do with converting to ()
, but is related to discarding a type that you really don’t want to discard since it represents lazy evaluation or effect capture
import scalaz.syntax.monad._
def debugPrint(msg: => String): Task[Unit] = Task.delay(logger.debug(msg))
def takeOffShoe: Task[Shoe] = ???
def throwShoe(shoe: Shoe): Task[Unit] = ???
def randomTask(): Task[Unit] = {
takeOffShoe >> debugPrint("shoe taken off") flatMap { shoe =>
throwShoe(shoe)
} // oops discarded the value here by mistake, forgot the >> and scalac doesn't help!
debugPrint("shoe thrown")
}
I don’t know if there is any answer to this example, but I thought it was relevant, as Task
is an example of an IO like type and would most certainly work well with an @noDiscard.
An example that is in line with the original post:
def debugLog(msg: => String): Task[Unit] = Task.delay(strictLogger.debug(msg))
def blah(): Unit = {
debugLog("doing stuff") // oops, user didn't realize the tool they are using is capturing an effect, not running it, or its a typo. This can not be caught by -Ywarn-value-discard
doStuff()
doMoreStuff()
debugLog("stuff done") // oops, user didn't realize the tool they are using is capturing an effect, not running it, or a typo this one can be caught by -Ywarn-value-discard
}
Which gets me back to the agreement that this seems to be about types and their intended use, not purity in general. It is also not strictly about coercion toUnit.
IMO, if Task was annoated with @noDiscard, then there would be two warnings above since a value of that type was produced but not used or stored or returned.
I’m not sure if any basic scala type is a candidate for this.
def f(): Try[Int] = Try { throw new Exception() }
def foo(): Try[Unit] = Try{
f() // silently discards the exception thrown
f() // also discards the exception but is caught by -Ywarn-value-discard
}
import java.lang.ref.WeakReference;
import java.util.*;
public class A {
public static void main(final String[] args) throws InterruptedException {
Object obj = new Object();
WeakReference<Object> ref = new WeakReference<Object>(obj);
List<byte[]> filler = new LinkedList<byte[]>();
while (ref.get() != null) {
filler.add(new byte[1000]);
}
System.out.println("Filler size " + filler.size());
}
}
$ java -Xint A
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at A.main(A.java:11)
$ java A
Filler size 126974