Proposal: Allow only multiline `<|` and `|>` expressions
- Dominant language
- Haskell
- Stars
- 1.3k
- Forks
- 147
- PR merge metrics
- No merged PRs in 30d
Description
## Motivation
A primary goal of `elm-format` has always been to prevent teams from having stylistic debates.
I've noticed an area where I still feel a desire to make comments in PRs for what is a purely stylistic choice: the use of inline `<|` and `|>`.
## Proposal
Have `elm-format` rule out these stylistic debates by permitting only multiline `<|` and `|>` expressions.
Consider this expression:
```elm
List.head (Set.toList mySet)
```
This would be unaffected by this proposal because it uses neither `<|` nor `|>`.
Here's another way to write an equivalent expression, which `elm-format` currently permits:
```elm
Set.toList mySet |> List.head
```
This proposal would have `elm-format` rewrite the above to this:
```elm
Set.toList mySet
|> List.head
```
Similarly, this would get rewritten...
```elm
List.head <| Set.toList mySet
```
...to this:
```elm
List.head <|
Set.toList mySet
```
## Scenarios with Clear Winners
For certain shapes of larger expressions, it's clear when `<|` or `|>` is the best way to write an expression. In scenarios where there is a clear winner, there's no meaningful room for debate.
For example, here are several ways to write the same expression:
```elm
Maybe.withDefault 0 (Maybe.map sqrt (List.head (List.reverse (getAverages (List.compact rawData)))))
Maybe.withDefault 0 <| Maybe.map sqrt <| List.head <| List.reverse <| getAverages <| List.compact rawData
Maybe.withDefault 0 <|
Maybe.map sqrt <|
List.head <|
List.reverse <|
getAverages <|
List.compact rawData
rawData |> List.compact |> getAverages |> List.reverse |> List.head |> Maybe.map sqrt |> Maybe.withDefault 0
rawData
|> List.compact
|> getAverages
|> List.reverse
|> List.head
|> Maybe.map sqrt
|> Maybe.withDefault 0
```
The last one is the clear winner; it reads from top to bottom as a sequence of transformations, and it reads in the order in which the transformations will be applied. It takes up more vertical space than the others, but it's been a general design principle of `elm-format` not to consider that a serious downside.
In practice, I haven't seen anything approaching a style debate for cases like this. People use `|>` in the multiline style in these situations, and it's great. This proposal wouldn't affect this case.
Here's another expression written a few different ways:
```elm
makeUser
(if String.isEmpty username then
"Anonymous"
else
username
)
(if String.isEmpty username then
"Anonymous"
else
username
)
|> makeUser
makeUser <|
if String.isEmpty username then
"Anonymous"
else
username
```
Again the last one is a clear winner. `<|` is the only operator that removes the need for multiline parens. This also comes up when passing anonymous functions (as `elm-test` does) or *case-expressions*.
I don't think this is a source of style debates either, and the proposal doesn't affect this case either.
## Scenarios with Debatable Winners
All the scenarios with clear winners involve using `<|` and `|>` in multiline expressions. What about single-line expressions?
Currently, any time you do single-line function application, you have a stylistic choice to make: write it as `foo bar` or `foo <| bar` or `bar |> foo`. In the specific case of `foo bar` it seems clear that introducing a `<|` or `|>` is silly; it makes the expression longer and does not add clarity.
What about in the case of `foo (bar baz)`? Here it's more debatable. Some people prefer `foo <| bar baz` because it doesn't use parentheses. Others prefer `bar baz |> foo` because depending on `bar` and `foo`, it may make more sense to think of this expression in terms of running `bar baz` and then running `foo` on the result, rather than in terms of "run `foo` passing the result of `bar baz`."
Really, if an expression is short enough to fit on a single line, how big a difference is there between these three?
```elm
Maybe.withDefault 0 (List.head list)
Maybe.withDefault 0 <| List.head list
List.head list |> Maybe.withDefault 0
```
There's plenty of room for debate here.
This is the case this proposal would change. It would rewrite the last two as multiline expressions—because as we've seen, it's valuable to support `<|` and `|>` in these multiline contexts—leaving the first way as the only way to write it on a single line.
In this way, `elm-format` would remove this source of stylistic debates by limiting the use of `<|` and `|>` to only the use cases where they are capable of being clear winners.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing elm-format's existing handling of inline and multiline <| and |> expressions, using the examples in the issue as behavioral cases. Determine how the proposed restriction should apply while preserving the multiline cases described, then verify that inline operators are rewritten and unaffected expressions remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elm, haskell
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100