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

Clarify lambda positioning #10

Merged
merged 5 commits into from
Jan 19, 2021
Merged

Clarify lambda positioning #10

merged 5 commits into from
Jan 19, 2021

Conversation

Smaug123
Copy link
Contributor

This is to help resolve an inconsistency in the report at fsprojects/fantomas#1189 .

@nojaf
Copy link
Contributor

nojaf commented Oct 26, 2020

@Smaug123 this is a good first example. I do have some follow-up questions on this, I'll get back on this later this week.

@nojaf
Copy link
Contributor

nojaf commented Oct 26, 2020

I'm guessing this also applies when no infix operators are involved?

let printListWithOffset a list1 =
    List.iter (
        ((+) a)
        >> printfn "%d"
    ) list1

Is list1 after the )?

What happens if there is an extra argument before the parenthesis?
Example:

let mySuperFunction a =
    someOtherFunction a (fun b ->
        // doing some stuff her
       b * b
    )

Did I assume correctly that the lambda is one further indented?

What happens when there a two multiline lambda's?

let mySuperFunction v =
    someOtherFunction (fun  a  ->
          let meh = "foo"
          a
     )  (fun b ->
              // probably wrong
              42
     ) v

@Smaug123
Copy link
Contributor Author

Smaug123 commented Oct 26, 2020

I think your examples are right except for the last one where there's too much indentation:

let mySuperFunction v =
    someOtherFunction (fun  a  ->
        let meh = "foo"
        a
     ) (fun b ->
        // probably wrong
        42
     ) v

But I am toying instead with:

let mySuperFunction v =
    someOtherFunction
        (fun  a  ->
            let meh = "foo"
            a
        )
        (fun b ->
            // probably wrong
            42
        )
        v

which at least generalises nicely to having arbitrary small arguments (e.g. we could move v one argument place earlier and it would be obvious what should happen).

@nojaf
Copy link
Contributor

nojaf commented Oct 26, 2020

I like the

let mySuperFunction v =
    someOtherFunction
        (fun  a  ->
            let meh = "foo"
            a
        )
        (fun b ->
            // probably wrong
            42
        )
        v

the idea, I'll ask some questions along the way to nail down the patterns that Fantomas would need to respect.

README.md Outdated
printfn "%d" (a + elem)
)

// OK
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Smaug123, what if I have something like

    innerFunc<'a, 'b>
        path'
        elementNameThatHasThisRatherLongVariableNameToForceTheWholeThingOnMultipleLines
        (foo >> bar value >> List.item a)
        shape

should this be

    innerFunc<'a, 'b> path' elementNameThatHasThisRatherLongVariableNameToForceTheWholeThingOnMultipleLines (
        foo >> bar value >> List.item a
    ) shape

Or is there a stricter rule in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the whole thing wants to split then, i.e. your first example:

    innerFunc<'a, 'b>
        path'
        elementNameThatHasThisRatherLongVariableNameToForceTheWholeThingOnMultipleLines
        (foo >> bar value >> List.item a)
        shape

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when is it

A:

let printListWithOffset a list1 =
    list1
    |> List.iter (
        ((+) a)
        >> printfn "%d"
    )

and when is it

B:

 innerFunc<'a, 'b>
        path'
        elementNameThatHasThisRatherLongVariableNameToForceTheWholeThingOnMultipleLines
        (foo >> bar value >> List.item a)
        shape

I'm still sort of looking for a technical rule so to speak.
A could be because:

  • No additional arguments are passed to List.iter
  • A pipe operator is preceding List.iter

B could be because:

  • Everything before (foo >> bar value >> List.item a) already makes the whole thing too long.
  • There are additional arguments next to (foo >> bar value >> List.item a), in A there are not.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's due to the fact that the lambda is the only argument (ignoring the one we are getting piped in).

For instance:

let foo =
    List.iter (
        ((+) a)
        >> printfn "%d"
    )

Would be allowed whereas:

let foo =
    someOtherThing a b c d (
       ((+) a)
       >> printfn "%d"
    ) f g h

Would be reformatted like B

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree: if there is just one argument that is too long, the whole thing gets split over many lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, the A example is a bit misleading. These are both right:

let printListWithOffset a list1 =
    list1
    |> List.iter (
        ((+) veryVeryVeryVeryVeryVeryVeryVeryVeryLongThing)
        >> printfn "%d"
    )

let printListWithOffset' a list1 =
    list1
    |> List.iter (((+) a) >> printfn "%d")

@Smaug123
Copy link
Contributor Author

@JackMatusiewiczGR Mind approving this again? I didn't know we had stale review dismissal enabled.

@nojaf
Copy link
Contributor

nojaf commented Dec 8, 2020

@Smaug123 how would

lambdaList
|> List.map (function
    | Abs(x, body) -> 1 + sizeLambda 0 body
    | App(lam1, lam2) -> sizeLambda (sizeLambda 0 lam1) lam2
    | Var v -> 1)

look like in this case?

@Smaug123
Copy link
Contributor Author

Smaug123 commented Dec 8, 2020

That structure is a little awkward. Personally I format it as:

lambdaList
|> List.map (
    function
    | Abs (x, body) -> 1 + sizeLambda 0 body
    | App (lam1, lam2) -> sizeLambda (sizeLambda 0 lam1) lam2
    | Var v -> 1
)

But I recognise this rule is rather ugly in how specific it is. (In this case only, I put function on a new line indented one scope, so that the indentation rules for the pattern match don't look absolutely mad.)

This doesn't really come up in GR's code, I think.

@Smaug123 Smaug123 merged commit 94fcee3 into master Jan 19, 2021
@Smaug123 Smaug123 deleted the clarify-lambda branch January 19, 2021 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants