Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curried functions with implicit parameter lists are always unfolded even though the verticalMultiline.arityThreshold is not reached. #3520

Closed
agilesteel opened this issue Mar 30, 2023 · 9 comments · May be fixed by #3524

Comments

@agilesteel
Copy link

agilesteel commented Mar 30, 2023

Configuration

version = "3.7.3"

runner.dialect = scala213

maxColumn = 100

newlines.implicitParamListModifierForce = [after]

verticalMultiline {
  arityThreshold = 3
  atDefnSite = yes
  newlineAfterOpenParen = yes
}

Steps

Given code like this:

object Foo {
  def bar(a: Int)(implicit b: Int): Int =
    a + b
}

Problem

Scalafmt formats code like this:

object Foo {
  def bar(
      a: Int
    )(implicit
      b: Int
    ): Int =
    a + b
}

Expectation

I would like the formatted output to look like this:

object Foo {
  def bar(a: Int)(implicit b: Int): Int =
    a + b
}

Workaround

I've found that by switching back all the way to 3.7.1 fixes the problem.

Notes

I think it's related to #3478

@kitbellew
Copy link
Collaborator

@agilesteel perhaps it's because 3.7.1 and before handled this incorrectly. the parameter is named implicitParamListModifierForce, hence the newline is forced, it cannot be omitted. if you try to format without vertical multiline, it will introduce a newline as well.

@agilesteel
Copy link
Author

Maybe this should be a feature request then? I think it makes sense to respect the threshold.

Changing it to newlines.implicitParamListModifierPrefer = after fixes the issue, but causes the following sometimes:

object Foo {
  def bar(
      a: Int
    )(implicit b: Int // b: Int should be on the next line
    ): Int =
    a + b
}

@kitbellew
Copy link
Collaborator

Maybe this should be a feature request then? I think it makes sense to respect the threshold.

The threshold forces the format, but it's not the only way; if the signature can't be formatted on one line (such as when a newline is forced), that will cause this formatting as well.

object Foo {
  def bar(
      a: Int
    )(implicit b: Int // b: Int should be on the next line
    ): Int =
    a + b
}

Why did you expect b: Int to be on the next line? The entire implicit b: Int fits on one line so newlines.implicitParamListModifierPrefer should not apply, according to the documentation.

Also, if dangling parens parameter is set appropriately, this will also happen:

object Foo {
  def bar(
      a: Int
    )(b: Int
    ): Int =
    a + b
}

The reason is that the formatter doesn't force a newline when indentation would be the same or larger, compare with:

object Foo {
  def bar(
      a: Int
    )(
      b: Int
    ): Int =
    a + b

  def bar(
      a: Int
    )(
      implicit b: Int // foo
    ): Int =
    a + b
}

@agilesteel
Copy link
Author

So how would I make it behave as it did before? Basically all I'd like to have is that it doesn't break the line immediately just because there is an implicit parameter involved, but when it does need to break the line either because the line is too long or the arity threshold is reached or for whatever reason, then it should break it the same as it does now in 3.7.3.

@kitbellew
Copy link
Collaborator

@agilesteel "it doesn't break" means you shouldn't use Force. can you demonstrate the second part with an example, please?

@agilesteel
Copy link
Author

agilesteel commented Apr 27, 2023

So I'm copy pasting this from the problem section in this issue.

object Foo {
  def bar(
      a: Int
    )(implicit
      b: Int
    ): Int =
    a + b
}

I like HOW the formatting is done here. This is how it was both before 3.7.3 as well as it is now in 3.7.3 (assuming the conditions in the next paragraph were met). The only difference is WHEN formatting should happen.

Before no formatting was occurring unless the line was too long or unless my arity threshold was reached, so sth like the following would remain untouched:

object Foo {
  def bar(a: Int)(implicit b: Int): Int =
    a + b
}

You said this used to be due a bug that has now been fixed. I guess I shouldn't use force anymore, but what do I use to make it behave like it did before?

@kitbellew
Copy link
Collaborator

So I'm copy pasting this from the problem section in this issue.

object Foo {
  def bar(
      a: Int
    )(implicit
      b: Int
    ): Int =
    a + b
}

I like HOW the formatting is done here. This is how it was both before 3.7.3 as well as it is now in 3.7.3 (assuming the conditions in the next paragraph were met). The only difference is WHEN formatting should happen.

Before no formatting was occurring unless the line was too long or unless my arity threshold was reached, so sth like the following would remain untouched:

object Foo {
  def bar(a: Int)(implicit b: Int): Int =
    a + b
}

You said this used to be due a bug that has now been fixed. I guess I shouldn't use force anymore, but what do I use to make it behave like it did before?

nothing currently. implicit b: Int fits on a line and hence will not cause a break within.

@agilesteel
Copy link
Author

Right, which brings us back to Maybe this should be a feature request then? 😉

@kitbellew
Copy link
Collaborator

please discuss this proposal on the scalafmt discord channel, and re-open if the community supports this.

@kitbellew kitbellew closed this as not planned Won't fix, can't repro, duplicate, stale Jun 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants