35

I know that Java has smart/lazy evaluation in this case:

public boolean isTrue() {
    boolean a = false;
    boolean b = true;
    return b || (a && b); // (a && b) is not evaluated since b is true
}

But what about:

public boolean isTrue() {
    boolean a = isATrue();
    boolean b = isBTrue();
    return b || a;
}

Is isATrue() called even if isBTrue() returns true?

m0skit0
  • 25,268
  • 11
  • 79
  • 127
  • A side note regarding the && part of your example: `return a && b` might ignore b if a is false, but `return a & b` will also execute the second part of the relation. – Simeon Mar 15 '21 at 11:07

9 Answers9

35

Well, as far as the language is concerned - yes, both functions are called.

If you rewrote the function to this:

public boolean isTrue() {
    return isBTrue() || isATrue();
}

then the second function will not be called, if the first is true.


But this is short-circuit evaluation, not lazy evaluation. Lazy evaluation case would look something like this:

public interface LazyBoolean {
    boolean eval();
}

class CostlyComparison implements LazyBoolean {
  private int a, b;

  public CostlyComparison(int a, int b) { 
    this.a=a; 
    this.b=b; 
  }

  @Override 
  public boolean eval() {
    //lots of probably not-always-necessary computation here
    return a > b;
  }
} 

public LazyBoolean isATrue() {
  return new CostlyComparison(10,30);  //just an example
}

public boolean isTrue() {        // so now we only pay for creation of 2 objects
    LazyBoolean a = isATrue();   // but the computation is not performed; 
    LazyBoolean b = isBTrue();   // instead, it's encapsulated in a LazyBoolean
    return b.eval() || a.eval(); // and will be evaluated on demand;
                                 // this is the definition of lazy eval.
}
emesx
  • 12,555
  • 10
  • 58
  • 91
  • 7
    *"But this is short-circuit evaluation, not lazy evaluation."* Well no, actually it is: *"short-ciruit evaluation simply is (a very limited case of) lazy evaluation"*. – m0skit0 May 19 '14 at 08:30
26

In Java (and other C-like languages), this is referred to as short-circuit evaluation.*

And yes, in the second example isATrue is always called. That is, unless the compiler/JVM can determine that it has no observable side-effects, in which case it may choose to optimize, but in which case you wouldn't notice the difference anyway.


* The two are quite distinct; the former is essentially an optimization technique, whereas the second is mandated by the language and can affect observable program behaviour.

I originally suggested that this was quite distinct from lazy evaluation, but as @Ingo points out in comments below, that's a dubious assertion. One may view the short-circuit operators in Java as a very limited application of lazy evaluation.

However, when functional languages mandate lazy-evaluation semantics, it's usually for a quite different reason, namely prevention of infinite (or at least, excessive) recursion.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • There's a tag for "lazy-evaluation", so I guess it's a thing too. – Captain Ford Mar 03 '13 at 18:35
  • 1
    @CaptainFord: yup, I've provided Wikipedia links to both in my answer ;) – Oliver Charlesworth Mar 03 '13 at 18:36
  • Thanks for the correction, you're right. *unless the compiler/JVM can determine that it has no observable side-effects* In this case it can determine I think, so why is `isATrue()` always called? – m0skit0 Mar 03 '13 at 18:39
  • 1
    @m0skit0: Because it hasn't bothered to optimize in this case? How have you determined that it's been called? – Oliver Charlesworth Mar 03 '13 at 18:39
  • Point taken. Related, but not the same. – Captain Ford Mar 03 '13 at 18:41
  • @m0skit0 because the function `isXTrue` might not be final, because in one class in might always return true in others true/false in random fashion.. Depending on the case, the JVM can optimize this, inline the functions or do whatever it wants. HotSpot is known for aggressive inlining and it detects situations when even non-final functions have only one implementations, so they're effectively final – emesx Mar 03 '13 at 18:48
  • 2
    I didn't downvote, but you deserved it. Because short-ciruit evaluation simply *is* (a very limited case of) lazy evaluation. – Ingo Mar 05 '13 at 10:45
  • 1
    From the link you provided: *In languages that use lazy evaluation by default (like Haskell), all functions are effectively "short-circuit", and special short-circuit operators are unnecessary.* How would this be possible, if short circiuit evaluation is not subsumed by lazy evaluation? – Ingo Mar 05 '13 at 10:48
  • 1
    @Ingo: Yes, you're right. When I wrote the above answer, I was referring to lazy-evaluation in the sense of optimization (and in this context I maintain that it's distinct from short-circuit). But indeed, in certain languages (particularly functional) it's basically mandated, and affects program semantics. I'll see if I can reword my answer to restore order! – Oliver Charlesworth Mar 05 '13 at 12:50
  • Now you got my upvote. And yes, of course, both lazy evaluation and short circuit evaluation are all about semantics - like order of execution and if something is executed at all. Difference is, in Java, C, etc. it is some *magic* in the compiler - in fact, you cannot abstract from a series of || and && operations and make it a function without changing the semantics. – Ingo Mar 05 '13 at 14:14
17

No, Java has only eager evaluation for user-defined methods. Some of Java's language constructs implement non-strict evaluation as you note. Others include if, ?:, while.

I once learned[1] that there is some confusion around what it means to "have lazy evaluation." First, lazy evaluation means call-by-need evaluation. Java has nothing at all like this. However, there is a common trend (which I personally discourage) to loosen the definition of lazy evaluation to also include call-by-name evaluation. Functions such as && cannot be distinguished under call-by-need versus call-by-name evaluation; it would be the same regardless, which obscures the matter.

Taking this loosening into account, some further counter-argue by claiming Java has lazy evaluation by the following:

interface Thunk<A> {
  A value();
}

Then, you might write a user-defined && like so:

boolean and(boolean p, Thunk<Boolean> q) {
  return p && q();
}

The claim is then put forward that this demonstrates that Java has lazy evaluation. However, this is not lazy evaluation, even in the loose sense. The distinguishing point here is that Java's type system does not unify the types boolean/Boolean and Thunk<Boolean>. Attempting to use one as the other will result in a type-error. In the absence of a static type system, the code would still fail. It is this lack of unification (static typing or not) that answers the question; no, Java does not have user-defined lazy evaluation. Of course, it can be emulated as above, but this is an uninteresting observation which follows from turing-equivalence.

A language such as Scala has call-by-name evaluation, which allows a user-defined and function which is equivalent to the regular && function (taking termination into account. See [1]).

// note the => annotation on the second argument
def and(p: Boolean, q: => Boolean) =
  p && q

This allows:

def z: Boolean = z
val r: Boolean = and(false, z)

Note that this short program snippet terminates by providing a value. It also unifies values of type Boolean as call-by-name. Therefore, if you subscribe to the loose definition of lazy evaluation (and again, I discourage this), you might say that Scala has lazy evaluation. I provide Scala here as a good contrast. I recommend looking at Haskell for true lazy evaluation (call-by-need).

Hope this helps!

[1] http://blog.tmorris.net/posts/a-fling-with-lazy-evaluation/

Tony Morris
  • 3,045
  • 2
  • 29
  • 17
13

SE8 (JDK1.8) has introduced Lambda expressions, which can make lazy evaluations more transparent. Consider the statement in the main method of the following code:

@FunctionalInterface
public interface Lazy<T> {
   T value();
}

class Test {
   private String veryLongMethod() {
      //Very long computation
      return "";
   }

   public static <T> T coalesce(T primary, Lazy<T> secondary) {
      return primary != null? primary : secondary.value();
   }

   public static void main(String[] argv) {
      String result = coalesce(argv[0], ()->veryLongMethod());
   }
}

The called function coalesce returns the first given non-null value (as in SQL). The second parameter in its call is a Lambda expression. The method veryLongMethod() will be called only when argv[0] == null. The only payload in this case is inserting ()-> before the value to be lazily evaluated on demand.

Dima
  • 131
  • 1
  • 3
  • 1
    @pagoda_5b re: 'this does not answer the OP'. Actually it does. JDK8 lambda expressions can be used for lazy function evaluation in addition to any mechanisms provided in earlier JDK versions. The answer, although incomplete, is on the money, so it is wrong to say it does not answer the OP. – aem999 Jul 18 '15 at 06:06
  • I removed the original comment. You're probably right – pagoda_5b Jul 18 '15 at 12:23
  • 2
    I would use the existing java.util.function.Supplier as the functional interface, but perhaps creating a functional interface called Lazy more clearly shows the intent. – Henno Vermeulen Nov 02 '15 at 09:33
3

For simplicity you can use the Supplier interface from java 8 like this:

Supplier<SomeVal> someValSupplier = () -> getSomeValLazily();

Then in code afterwards you can have:

if (iAmLazy) 
  someVal = someValSupplier.get(); // lazy getting the value
else 
  someVal = getSomeVal(); // non lazy getting the value
George
  • 7,206
  • 8
  • 33
  • 42
3

Just wanted to add in addition to what have been mentioned in this question thread, below is from Oracle Documentation on JVM

a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized.

reference

and as an example of the classes that has lazy implementation is Stream, this is from Oracle Documentation on Stream

Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

reference

That being said, if you do the following, nothing will be displayed. Unless you add initiator.

Stream.of(1, 2, 3, 4, 5).filter(number -> {
   System.out.println("This is not going to be logged");
   return true;
});
Rohit Banga
  • 18,458
  • 31
  • 113
  • 191
Abdullah Shahin
  • 1,002
  • 15
  • 19
0

Is isATrue() called if isBTrue() returns true?

Yes, both are called.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
0

No, it does not. isBTrue() will be invoked, regardless of the result of isATrue(). You can verify this yourself by writing such a program, with print statements in each of the methods.

Jeremy Roman
  • 16,137
  • 1
  • 43
  • 44
  • 2
    But if I punt a print statement then the compiler/JVM will feel forced to call them, and won't optimize :) – m0skit0 Mar 03 '13 at 18:37
  • 1
    The semantics of the Java language expect them to be called. If the compiler can with certainty safely eliminate the call without affecting the program semantics (i.e. changing behaviour), it may do so. I don't think the JVM actually does, though. – Jeremy Roman Mar 03 '13 at 18:39
0

Yes isATrue() will be called because you are calling it explicitly in line boolean a = isATrue();

But it won't be called in following case if isBTrue() returns true:

public boolean isTrue() {
    return isBTrue() || isATrue();
}
Vishal K
  • 12,976
  • 2
  • 27
  • 38
  • In true lazy languages (usually functional languages, e.g. Haskell) `isATrue()` wouldn't be called because the variable `a` wouldn't be actually computed until it is actually used (which is effectively never). – m0skit0 May 04 '15 at 22:11