Skip to content

Clarify lambda positioning#10

Merged
Smaug123 merged 5 commits intomasterfrom
clarify-lambda
Jan 19, 2021
Merged

Clarify lambda positioning#10
Smaug123 merged 5 commits intomasterfrom
clarify-lambda

Conversation

@Smaug123
Copy link
Copy Markdown
Contributor

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

@nojaf
Copy link
Copy Markdown
Member

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
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
Member

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.

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

// OK
Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
Member

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.

Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor Author

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

@nojaf
Copy link
Copy Markdown
Member

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
Copy Markdown
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