scalameta / metals-feature-requests

Issue tracker for Metals feature requests
37 stars 4 forks source link

Worksheet: Show values inside for comprehensions #198

Open phdoerfler opened 3 years ago

phdoerfler commented 3 years ago

Relation to a Problem

When I mess with collections I usually start with lines.map(...) which slowly turns into lines.map(...).map(_.map(...)) etc. at which point I decide: This should really be a for comprehension!

So I start all over again to rewrite it as one. This has one big drawback (1): I get very little support from Metals along the way. Here's a particularly messy comprehension: Maybe you can already feel the pain that was writing this comprehension.

Metals only shows the end result. IF it compiles. And that is a big if: More often than not it doesn't because of some mistake somewhere inside the for comprehension.

Had I stuck with my original messy chain of nested map, flatMap and filter I could easily rearrange it to see the results of the individual transformation steps aiding in figuring out where it went wrong. (2)

(1) Actually, two: Ideally there was a refactoring for converting a chain of map and friends into a for loop and vice versa. But that's a (user) story for another time.

Preferred Solution

Just like Metals, in worksheet mode, evaluates lines with statements and annotates them with their value, I want Metals to do the same inside for comprehensions. Since there's only limited space available, I'd pragmatically only output those values for the first element of the collection. I could see how it might be useful to produce a table with the intermediate results of all or the first "few" elements of the collection, but one thing at a time.

Alternatives I have considered

A more tedious solution to this problem would be a refactoring as described in (2) and then a manual rearrangement.

Search Terms

scala for comprehension inspection mdoc metals visual studio code worksheet

tgodzik commented 3 years ago

Thanks for reporting! I think this might be cool to implement, especially to support Futures etc. This would need to be done in https://github.com/scalameta/mdoc

olafurpg commented 3 years ago

Thank you for reporting! I think this is an interesting proposal and it's technically possible to implement this. The biggest open question IMO is how to display the values of expressions that evaluate multiple times

@phdoerfler can you provide a concrete example of how the decorations should look like inside for comprehensions?

Since there's only limited space available, I'd pragmatically only output those values for the first element of the collection. I could see how it might be useful to produce a table with the intermediate results of all or the first "few" elements of the collection, but one thing at a time.

I think we should aim for a complete solution from the start. If only the first value is displayed to begin with, I suspect many users will quickly report feature requests to display more values. If we change it to display multiple values then some people will ask for a configuration setting to only display the first value "like it used to work before".

Down the road, I imagine that we can extend the same solution to support more constructs than for comprehensions. For example, for local variables inside methods and lambdas

def increment(i: Int): Int = {
  val result = i + 1 // : Int = 11, 12
  result
}

increment(10)
increment(11)

List(1, 2).map { i =>
  val j = i + 2 // : Int 3, 4
  j
}

I think it makes sense to try and think of other syntaxes where the same functionality would be useful.

kubukoz commented 3 years ago

https://github.com/eed3si9n/expecty does something smart about expressions to capture intermediate values - maybe mdoc could apply some of that for this?

phdoerfler commented 3 years ago

Apple Swift Playgrounds might also be a source of inspiration.