Elm-format: Sorting things alphabetically

Created on 20 Oct 2015  Β·  33Comments  Β·  Source: avh4/elm-format

Should import statements be sorted? Should listings in the import statements and/or module statements be sorted?

What does sorted mean? Generally alphabetical sort is locale-dependent, but we want the output of elm-format to be the same regardless of the locale.

discussion

Most helpful comment

the first code I tried to format outside of Evan's/Richard's does what Jeff is describing of grouping and commenting imports: https://github.com/TheSeamau5/elm-check/blob/master/src/Check.elm#L66-L84

Oh! Here's an idea. This would be more work to implement, but would solve both cases:

  1. Treat any contiguous (no blank lines in between) sequence of import statements as a "group"
  2. Alphabetize imports within each group

This would mean that those who want to group can do so, and those of us who don't want to think about it can just slap our imports onto the end of the running list, and elm-format will sort them into a consistent place so we don't have to think.

All 33 comments

@Apanatshka:

Are you sorting the imports alphabetically? Not sure how I feel about that.. Depending on how many inputs people have, they may want to group them themselves.

@rtfeldman:

That is a tradeoff, true, but it's a good sign that this is exactly the tradeoff that makes a formatter exciting: sacrificing a bit of flexibility for the sake of no longer having to spend mental energy organizing something. :)

I think until there is a fully automated way to handle imports*, the formatter should try to preserve the formatting of the imports. Perhaps you can keep the sorting feature as opt-in.

*This is what you have in Java IDEs. The imports are collapsed in the editor and handled automatically by the IDE. Nobody cares what's there exactly as long as stuff works.

I feel strongly the other way. I've tried a variety of different importing styles, and the only thing that's been at all useful is grouping module namespaces together (e.g. Html, Html.Attributes, and Html.Events being adjacent) - which alphabetizing covers automatically.

As for the other grouping strategies I've tried (groups like "in-house modules vs third-party" or "modules concerned with UI vs. general logic modules" or "open vs qualified imports") I've looked back on 100% of these extra groupings as wasted time. I tried to do better than alphabetizing and failed hard.

This is exactly the sort of alluring time sink I'm excited to have elm-format save me from!

Is there a particular grouping you've used in the past that you felt was really useful, that alphabetizing would preclude?

I personally agree right @rtfeldman, but @Apanatshka's argument is probably shared by many others.

Notably, the first code I tried to format outside of Evan's/Richard's does what Jeff is describing of grouping and commenting imports:

https://github.com/TheSeamau5/elm-check/blob/master/src/Check.elm#L66-L84

Also, I'm concerned about sort order. Elm identifiers can include some unicode characters (like Δ“), but not others (like ❖). As near as I can tell from the source code, Elm allows identifiers to contain "lower-case alphabetic Unicode characters (letters)," "upper-case or title-case alphabetic Unicode characters (letters)," but NOT non-letters, and NOT "letters of caseless scripts and modifiers letters." We will need to define rules that work appropriately for all identifiers.

Also, "alphabetically" is locale-specific. (For example, in Lithuanian, "y" is sorted between "i" and "k" [1].) I think we want elm-format to produce the same output on any machine, and in any locale, so we need to define which "alphabetically" we would use, and consider whether such a choice will make sense for all users.

the first code I tried to format outside of Evan's/Richard's does what Jeff is describing of grouping and commenting imports: https://github.com/TheSeamau5/elm-check/blob/master/src/Check.elm#L66-L84

Oh! Here's an idea. This would be more work to implement, but would solve both cases:

  1. Treat any contiguous (no blank lines in between) sequence of import statements as a "group"
  2. Alphabetize imports within each group

This would mean that those who want to group can do so, and those of us who don't want to think about it can just slap our imports onto the end of the running list, and elm-format will sort them into a consistent place so we don't have to think.

Regarding sort order, I think what's important is:

  1. Identical module namespaces end up adjacent (e.g. Html is next to Html.Attributes and Html.Events)
  2. When I add N imports, the VCS diff is just N additions (in other words, adding things won't reorder other things).
  3. It works the same way across all locales.

I believe "sort using Haskell's default Ord implementation for String in EN-US" would fit the bill. :smiley:

I like the idea of sorting groups independently. I'm going to postpone this for now, since I have to deal with preserving the groups and comments within the import section. After that's done, it should be trivial to add sorting of the groups (like a 1-line change).

I'd like to group imports by meanings, that's why I do not support this feature.

@gyzerok You can still group imports by meaning with @rtfeldman's suggestion:

  • Treat any contiguous (no blank lines in between) sequence of import statements as a "group"
  • Alphabetize imports within each group

I came here to raise an issue titled "please sort imports alphabetically so I don't have to"

Here is an example of my imports

import Lib exposing (..)
import Vector exposing (..)
import Game.Data exposing (..)
import Game.Keyboard exposing (..)
import Game.Maps exposing (..)
import GameData.Tile exposing (..)
import Dict exposing (..)

The only problem would be if I really wanted to group Dict and Vector, but if that was the case, it'll likely be some modules I've written and I can always put them in one folder and then it'll be grouped by alphabetical sorting again.

@liamcurry I don't understand why do we need to introduce such complexity and workarounds? Things would be much simpler without this feature.

@gyzerok I respectfully disagree. The whole point of elm-format has been opinionated formatting of my code. If I write code, elm-format does it's thing to make it readable as the file size grows.

Right now it does nothing to imports and I have to _manually_ group or sort them. This is contrary to everything else it does for me, so why not handle tidying up of imports?

@liamcurry currently, elm-format puts spaces around single line comments --, if these were used to 'group' imports, we'd not be creating another convention?

e.g this is how it separates imports with -- in the middle

-- Character creation

import CharCreation.CharCreation as CharCreation exposing (..)
import CharCreation.Data exposing (..)


-- Main game screen

import Game.Game exposing (..)
import Game.Data exposing (..)


-- Cotw specific data

import CotwData exposing (Msg(..), Page(..))


-- Keyboard/Controller subscriptions

import Game.Keyboard exposing (..)


-- Core/Elm imports

import Html exposing (..)
import Html.App exposing (map)

I'm in favor for preserving empty lines between import statements as @rtfeldman suggested.

I'm usually grouping import statements by namespaces, for example:

import Html exposing (node, text)
import Html.Keyed
import Html.App

import Dict exposing (Dict)
import Date

import Ui.DatePicker
import Ui.Checkbox
import Ui.Chooser
import Ui.Input

After formatting this turns into:

import Html exposing (node, text)
import Html.Keyed
import Html.App
import Dict exposing (Dict)
import Date
import Ui.DatePicker
import Ui.Checkbox
import Ui.Chooser
import Ui.Input

The only difference here are the new lines, which I'm re-adding by hand every time I make a commit.

As for the ordering I strongly disagree of force ordering of anything, as you can see I usually go from longest line to shortest line so this as well boils down to preference.

Can people who spend time grouping and ordering their imports please give more insight into why they do that. Is it just that it looks nice? Do you typically read all the imports when you go to a file you haven't worked on in a while? Is it so that you can quickly find the right line if you need to delete or modify it?

I'd like to have some actual reasoning beyond "this is just what I do".

Pretty much these (in order):

Is it so that you can quickly find the right line if you need to delete or modify it?
Is it just that it looks nice?

Discussion from #279 should continue here.

The following questions need to be answered, here's my summary thus far:

  • What should the sort order be?

    • It seems fine to use whatever standard string sort haskell provides, and if we want to worry about unicode sort order, that can be dealt with later.

  • Should there be grouping, and how should it work?

    1. no grouping

    2. groups are separated by comments

    3. groups are separated by whitespace

  • Should the sort be based on the name of the module or the as alias (if there is one)?

And again, rather than stating personal preferences, I'd like to get a sense of what we're trying to accomplish. The potential goals that have been listed so far are:

  • To make it quick to find the right import line to modify or delete

Since this thread seems to be asking about different opinions I would like to share mine. I really like the way stylish-haskell has implemented imports sorting for Haskell. I think it is a very useful feature that works well with the spirit of elm-format and I was surprised it wasn't already there when I started using elm-format.

A few reasons I find it useful:

  • As already stated

    To make it quick to find the right import line to modify or delete

    It makes it easy to find the right import in massive lists of imports (even in a new code base)

  • Adding a new import is trivial, just add it to the bottom and it'll get sorted automatically

  • And more generally it forces a unique way of doing things making it easier for several programmers to share code.

For which sorting to use I think Haskell's default is good enough. I will probably never encounter a case where the default is wrong in my code but I can understand this could be an issue for other locales. Don't most people program in English even in non English speaking countries? I would be interested in hearing about some actual module names that would be badly sorted by Haskell's defaults.

I think grouping of the imports is important as I often separate local imports from library imports. stylish-haskell does this grouping by separating on newlines. At the moment elm-format removes newlines in between imports except when there are comments. Forcing the use of comments to separate groups might be a better way to keep the elm-format behaviour similar to what it currently is.

One thing to note here is that comments have to refer to groups and not singular imports (or that import would have to be in a group of its own).

Using the alias instead of the module name for sorting might be the most controversial choice here (and I don't think stylish-haskell does that). It allows for the imports to be sorted by the names they're used by in the code rather than the module name they're importing. This makes it easier to find the right import from looking at its usage in the code but might be confusing at first. It also has the advantage of grouping imports that share the same alias.

Not mentioned here is the sorting of the exposed functions in an import statement. Has this been discussed in a separate issue thread? Should a new issue be created for that?

I have opened PR #279 and would love to hear what choices would help the greatest number of people. I will change, adapt or completely remove the PR depending what you think. How will a decision be reached? Will there be some kind of vote?

To make it quick to find the right import line to modify or delete

I wouldn't consider this as a reason since it's actually IDE/Editor related thing. Even if you are using notepad, you are able to find import quickly using search functionality.

I would also opt for the automated sorting of imports (and probably union types too). This reduces the number of conflicts when working on an area of code with multiple people. If I add an import and my colleague also adds an import we might tend to always add them at the bottom. With alphabetical sorting the likelihood of a conflict might be somewhat reduced.

Another reason why I would opt for this is that it takes the argument out of our pull requests: "why did you group/sort the imports this way or another?" -> The formatter enforces a consistent sorting across all files.

Union type constructors probably shouldn't be sorted, since someday they
might be comparable based on declaration order. (I believe that's how
Haskell does it.)

On Fri, Dec 9, 2016, 1:29 AM Felix Lamouroux notifications@github.com
wrote:

I would also opt for the automated sorting of imports (and probably union
types too). This reduces the number of conflicts when working on an area of
code with multiple people. If I add an import and my colleague also adds an
import we might tend to always add them at the bottom. With alphabetical
sorting the likelihood of a conflict might be somewhat reduced.

Another reason why I would opt for this is that it takes the argument out
of our pull requests: "why did you group/sort the imports this way or
another?" -> The formatter enforces a consistent sorting across all files.

β€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/avh4/elm-format/issues/8#issuecomment-265970724, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABCxwN0jcM1YH-_aBcE-UZZHtO3B9fkmks5rGR-SgaJpZM4GR4fc
.

Not sure if I used the correct terminology I meant that the different cases of a type alias (enum) should maybe also be sorted:

type Msg
    = first
    | second
    | third

rather than:

type Msg
    = third
    | first
    | second

Right, that's what I meant too. ☺️

On Fri, Dec 9, 2016, 7:43 AM Felix Lamouroux notifications@github.com
wrote:

Not sure if I used the correct terminology I meant that the different
cases of a type alias (enum) should maybe also be sorted:

type Msg
= first
| second
| third

β€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/avh4/elm-format/issues/8#issuecomment-266044874, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABCxwALT4TmmYBVHbOetV8av7aEK2ISgks5rGXcogaJpZM4GR4fc
.

Oh, one more thing I noticed. Elm package installer sorts the dependencies in the elm-package.json. Which I like, so why not let elm-format sort the imports in source?

Managing imports is currently one of the bigger pain-points when working in elm, for me at least. While I favor not preserving whitespace when re-ordering imports (staying true to the tenets of doing everything consistently), having anything to help would be nice. As far the convention I try to use with ordering:

  • Standard library imports (those originating from elm-lang repo)
  • Whitespace
  • Other elm-package dependencies
  • Whitespace
  • Inter-project dependencies

I realize my preferred ordering would require the formatter to determine where the imports originate from, which is probably out-of-scope. In my ideal world though, the formatter would have this capability as well as the ability to remove unused imports.

One thing I would like to add into the discussion is the concept of developing on the same codebase with a team. In a group it is much harder to manage (personal) preferences and/or guidelines.
I think when elm-format would just sort the imports, without any freedom, this would make things easier:

  • Everybody can find the imports. You can easily scan to the correct module vertically.
  • No comments on pull requests like "You should move this import line 2 lines down" or "This import line does not belong to this group"

This Elm experience report touches on this issue:

With six developers and no tools to enforce import order, the files we created had inconsistent opinions on the order in which imports should appear. This inconsistency occasionally led to avoidable merge conflicts.
We need a tool to order imports in a predictable and consistent way, and elm-format seems like the appropriate tool for the job. How it orders imports isn't as important as every Elm file having its imports ordered consistently.

I hadn't considered merge conflicts as an argument for "sort consistently and don't permit custom whitespace groupings," but it seems like a compelling one to me.

@stoeffel mentioned a good point: if imports are sorted alphabetically, it's important that Native modules go last (and possibly not be sorted? πŸ€” Not sure if order of Native modules can matter, other than their coming after non-Native).

Ha! I just ran into the merge conflict case myself. I'd added something to the end of an import list, and so had someone else, resulting in this merge conflict:

import Http
import Rollbar exposing (Rollbar)
<<<<<<< HEAD
import Maybe.Extra
=======
import ViewUtil exposing (ErrorList)
>>>>>>> Use ErrorList

Sorted imports wouldn't dodge this in 100% of cases, but they totally would have in this case! πŸ˜„

:) And even if these occur. Both developers would resolve the conflict in the same way avoiding yet another conflict when merging back into master.

This also reduces the number of useless commits changing the order of the imports accidentally. This would make the git log and blame clearer. Making it easier to answer: why was this module added?

Going back to @mordrax's comment - I think "group via comments" as opposed to "group via whitespace" makes the most sense.

Reasons:

  1. Comments in between imports are syntactically valid. This case has to be taken into consideration regardless of whether there is an additional "group by whitespace" feature.
  2. All the same arguments that apply to whitespace groupings seem to make sense for comment groupings as well, so "sort each comment-group alphabetically" makes sense to me.
  3. The question then becomes: "should you additionally be allowed to sort by whitespace, without comments?" Put another way, "should you be able to make groups without using a comment to label what the groups mean?" That does not sounds like a compelling feature request. πŸ˜…

This is implemented in e4579233c0c99c23dbb0f2d0b8d7ba0fa856affc...5534bf66

Was this page helpful?
0 / 5 - 0 ratings

Related issues

avh4 picture avh4  Β·  5Comments

rtfeldman picture rtfeldman  Β·  5Comments

avh4 picture avh4  Β·  4Comments

gabrielflorit picture gabrielflorit  Β·  7Comments

mordrax picture mordrax  Β·  6Comments