I'm the author of the article and very interested reading all the discussion.
Let me explain exactly what the issue is as there seems to have been a bit of confusion about my intentions.
I understand that by stepping into the lambda we are effectively stepping into a new frame analogous to 'stepping into a method' - therefore variables outside the frame will not be visible inside that frame.
But - if we are stepping into a for loop of sorts (or perhaps the lines() method of Files) - you do want to see the other variables in the enclosing method. And the point is that using the old constructs this is exactly what you were able to do.
Java is not functional enough (or at least the way I code isn't) that all the variables I need will be enclosed in the lambda.
In fact when you step into a lambda you retain the scope of the enclosing class (unlike an inner class), so for example when you call toString() you will be printing the toString() of the enclosing object. For this reason it makes sense that in the debugger you should not be considered to have stepped into a new frame when you drop into a lambda.
Ultimately this is all about usability and in real life I have found it extremely frustrating that I can't easily observe the values outside the lambda.
> therefore variables outside the frame will not be visible inside that frame.
You know that the debugger can traverse the stack and show variables for the outer frames (assuming the lambda is invoked down-stack, as in your example), right? This is just a visualization issue of the debugger you're using and not a problem with lambdas themselves.
Javascript debuggers had to deal with this (the scope chain) for much longer, that's why variable views of JS debuggers show the whole scope chain instead of just the local frame.
I'm not associated with them, but they asked me for an eclipse screenshot if I had one, when I reported this issue to them and suggested that they create a workaround in their UI. (FWIW it's an annoying issue, but not enough for _me_ to stop using lambdas.)
If you have time, could you make the lambda a multi line chunk of code, so that it's clear that the breakpoint is inside the lambda? I'll pass it on to them. Thanks!
Of course this only works for cases where the debugger can tie the lambda used as method argument to another stack frame, i.e. it doesn't help with async invocations. But in async cases one cannot expect the JVM to lug around the whole stack frame from the time it was created, that could lead to memory leaks.
And from what I've gathered the Jetbrains bug may not talking about the same thing as the blog postis discussing. Their case is about a captured variable that gets inlined in some way that makes debugging difficult, OP is about inspecting arbitrary, non-captured variables in the debugger.
The lambda effectively is an anonymous class implementing that interface / that single method. U resolves to ? super String. I.e. it expects a method that takes String or a superclass thereof as its first parameter.
That's what e is. It all gets automatically pieced together at compile time via type inference.
Looks like this is an IDE issue. When C# implemented lambdas almost 8 years ago, the initial debugging was a little confusing. But quickly VS was improved to support better integration. Just give some time for the IDEs to catch up.
I would love to be able to evaluate lambdas in the debugger watch window in Visual Studio. Something like list.First(i=>i.Value==someValue) requires using a temporary variable, at least in VS2013.
I'm not sure this is a great argument. Lambdas are great for many things. Replacing old fashioned for loops isn't one of them. You get something harder to read, harder to debug and which is less efficient so why would you do that? If you aren't parallelising things there is little point.
Lambdas are a nice way to reduce the boilerplate associated with anonymous inner classes, and that's about the limit of how I use them.
With more of an FP background (erlang / haskell) I was a little confused regarding when you would need to inspect variables outside the lambda. Won't the external variables be the same as they were before you called forEach? Why do you want to check that each iteration?
Then I realized that mutating variables is fairly common in a loop in procedural programming and that if you were to translate that to a forEach it would be useful to see what was going on.
I think the introduction of FP concepts in Java 8 is going to be interesting when it is not accompanied by immutability and other foundations in the language (or at least idiomatic usage like in Scala). I can't tell if that will cripple them or make them more accessible.
His complaint isn't about not seeing mutating values each iteration, but that he can't see them at all. It's very common to want to know what's in the surrounding code, especially in the giant, incompletely specified and documented lumps of software characteristic of Java.
It's not like Java's the first imperative language to include FP concepts. Whatever lessons it can teach us, we could probably learn them just as well from Ruby, Python, C++, Javasript, C#, and so forth.
True, but those languages have a strong set of idioms for coding. For example, even though Ruby supports mutable calls, they are generally the exception and denoted with a ! (eg map vs map!) Meanwhile, Python is idiomatically opposed to map (to the point of intentionally crippling lambdas) in favor of list comprehensions.
My concern is whether Java8 will spark a strong change in idiomatic usage or if it will just be another incremental change to the current status quo.
This reminds me of how spoilt I am, with features such as edit-and-continue in Visual Studio. Doing some work recently with a more modern project that uses lambdas, edit and continue is not supported for code in lambdas. I miss the more interactive mode of writing code while executing it in the debugger. A more modern version of a REPL if you will.
Eclipse has had this feature for many years. Not only can you edit and continue, but you can actually restart a function from the beginning ("Drop to frame").
Yes, and visual studio lets you drag your current line pointer at any line within a certain scope, so if the first part of your function does some set-up calculations, no need to rerun after changing, just set your current line pointer at the line below and voila!
I know eclipse lets you jump up and down stack frames during debugging, I assume IDEA lets you do the same. So, while inconvenient, couldn't you jump up to the calling frame and inspect the variable in question from its scope?
There's no guarantee the method that created the lambda is still on the stack. If a method returns a callback, the method that created the lambda has returned before the lambda is called.
But wouldn't the stacktrace still include the file and line number where the lambda was created? Yes, you can't easily trace the code path between the creation of the lambda to its execution, but that strikes me as no different than debugging any other issue with bad data.
Yep, I confirm, intelliJ does know how to do that. I don't understand the rant either, so I'm going to assume he doesn't know how to use the stack on the bottom left of the debugger.
it's hardly java's fault he's looking at a stack frame and not seeing the variables that belong to a different stack. This is simply a matter of selecting a frame prior to the forEach.
Eclipse's debugger can be configured to show a refined output. The controls are in the upper right in the Debug Perspective. Programmers who make applications are not programmers who write IDE's. If I'd noticed this I don't know if I'd recognized it or dug into it more to understand it but a snarky comment like the one I replied to is not appropriate or helpful.
I disagree. The lambda expression allows him to use variables outside of the stack frame, why shouldn't the debugger also be able to show those same variables?
In Java, you can think of lambdas as sugar for anonymous classes. All problems and benefits that are present with anon classes should manifest with lambdas as well.
However, one clear advantage of lambdas is that they reduce verbosity of anon classes.
I like the verbosity of anon classes, they make sense. They look right in the debugger. Lambdas do not look clear they look like vodoo, I very much dislike them and wish they'd never been introduced.
Ironically using Scala lambdas - which are much more of a hack, being compiled to an $1 object with the captured variables as fields and the implementation as a method - I can see the variables when debugging just fine.
As I commented there, it should be very simple to switch the stack frame you are currently inspecting in the debugger. At least it's a piece of cake in C#, both in MonoDevelop and Visual Studio. OTOH, putting everything into every lambda's closure is inefficient. In the example in the article, the lambda did not close over anything in the outer scope, and can be compiled into a faster static method.
This is a weak argument. It's like saying we should avoid concurrency because it's harder to debug. Lambdas are a step forward and nothing short than that. I've been working with Java8 professionally for about 8 months and have trained about 5 teams on the new tooling. I've never seen anyone raise debugging their code as a reason to use an anonymous object instead of a lambda.
I think that is true in the case of forEach loops. However there are plenty of other situations in which the lack of lambdas has made something simple far more complicated. One example is the Strategy Pattern. With lambdas it's a fairly simple thing. Without lambdas there are a bunch of hoops you have to jump through.
Additionally, I feel that lambdas enable constructs that are much clearer and easier to reason about. Consider the case where you want to transform ever element in a list and produce a new list. Without the map function (which depends on lambdas) the logic wouldn't be that bad, you would create an empty list and append each element ad they are processed in a for loop. However, the intent of your code is less clear. Add in filtering and the intention of the code in your for loop ends up quite a bit harder to understand compared to a fairly straightforward select and map.
Let me explain exactly what the issue is as there seems to have been a bit of confusion about my intentions.
I understand that by stepping into the lambda we are effectively stepping into a new frame analogous to 'stepping into a method' - therefore variables outside the frame will not be visible inside that frame.
But - if we are stepping into a for loop of sorts (or perhaps the lines() method of Files) - you do want to see the other variables in the enclosing method. And the point is that using the old constructs this is exactly what you were able to do.
Java is not functional enough (or at least the way I code isn't) that all the variables I need will be enclosed in the lambda.
In fact when you step into a lambda you retain the scope of the enclosing class (unlike an inner class), so for example when you call toString() you will be printing the toString() of the enclosing object. For this reason it makes sense that in the debugger you should not be considered to have stepped into a new frame when you drop into a lambda.
Ultimately this is all about usability and in real life I have found it extremely frustrating that I can't easily observe the values outside the lambda.