alejandra
alejandra copied to clipboard
Allow new line(s) after `}:`
Here a (modified) example from nixpkgs containing a long argument list followed by a function:
{
lib
, autoPatchelfHook
, alsa-lib
, stdenv
, libXScrnSaver
, makeWrapper
, fetchurl
, wrapGAppsHook
, glib
, glibc
, gtk3
, unzip
, atomEnv
, libuuid
, at-spi2-atk
, at-spi2-core
, libdrm
, mesa
, libxkbcommon
, libappindicator-gtk3
, libxshmfence
, libXdamage
, nss
}:
version: hashes: let
name = "electron-${version}";
meta = with lib; {
description = "Cross platform desktop application shell";
homepage = "https://github.com/electron/electron";
license = licenses.mit;
maintainers = with maintainers; [ travisbhartwell manveru prusnak ];
platforms = [ "x86_64-darwin" "x86_64-linux" "i686-linux" "armv7l-linux" "aarch64-linux" ]
++ optionals (versionAtLeast version "11.0.0") [ "aarch64-darwin" ];
knownVulnerabilities = optional (versionOlder version "12.0.0") "Electron version ${version} is EOL";
};
in
true
It is clearly visible that a function with two parameters follows the argument list. alejandra does this:
{
lib,
autoPatchelfHook,
alsa-lib,
stdenv,
libXScrnSaver,
makeWrapper,
fetchurl,
wrapGAppsHook,
glib,
glibc,
gtk3,
unzip,
atomEnv,
libuuid,
at-spi2-atk,
at-spi2-core,
libdrm,
mesa,
libxkbcommon,
libappindicator-gtk3,
libxshmfence,
libXdamage,
nss,
}: version: hashes: let
name = "electron-${version}";
meta = with lib; {
description = "Cross platform desktop application shell";
homepage = "https://github.com/electron/electron";
license = licenses.mit;
maintainers = with maintainers; [travisbhartwell manveru prusnak];
platforms =
["x86_64-darwin" "x86_64-linux" "i686-linux" "armv7l-linux" "aarch64-linux"]
++ optionals (versionAtLeast version "11.0.0") ["aarch64-darwin"];
knownVulnerabilities = optional (versionOlder version "12.0.0") "Electron version ${version} is EOL";
};
in
true
The fact that there follows a function after the args list becomes hidden a bit.
Possible fix:
Always allow new lines (or even empty lines) after argument lists.
Downsides:
There will be multiple different styles allowed, vs right now there is only one single allows style, which has it's benefits as well.
Conclusion:
I'm not sure.
We can have the best of both worlds: enforcing the newlines, which means there is still one single style being allowed
The end result would be something like:
{
libXdamage,
nss,
}:
{
libXdamage,
nss,
}:
version: hashes:
{
libXdamage,
nss,
}:
Most times I've seen nested lambdas in practice is because they are logically separated concepts
We can have the best of both worlds: enforcing the newlines, which means there is still one single style being allowed
Should this be a universal rule, or just if followed by a function?
When the implementation is not another function it would look like a waste of space, but on the other hand, if we do it only for functions it would look inconsistent to the detail-oriented eye. If we allow choosing, then we sacrifice consistency as well, but overall would be an improvement since the user knows the perfect meaning
Other facts:
-
How common is this function inside function in Nix? not much common I'd say, I've normally seen it on frameworks, or library functions. More common in the side of people who produce code for other people to use
-
How do other languages deal with this function inside function? They sometimes inject the newlines to help distinguishing, sometimes not, there doesn't seem to be a common rule
I'm happy with all alternatives proposed so far, even leaving it as-is
imho, the example form https://github.com/kamadorueda/alejandra/issues/241#issuecomment-1058640106 should be formatted as:
{
libXdamage,
nss,
}:
{
libXdamage,
nss,
}:
version:
hashes:
{
libXdamage,
nss,
}:
this gives equal treatment to function arguments, regardless if they are attrset destructuring style, or simple identifiers
currently it's formatted as follows:
{
libXdamage,
nss,
}: {
libXdamage,
nss,
}: version: hashes: {
libXdamage,
nss,
}:
since simple identifier arguments are always collapsed into a single line (even if it means making the line very long):
here are some examples of how I'd prefer the formatter to behave in regards to function arguments:
in the following examples, developer "chooses" the formatting style, and the formatted enforces consistency: if all arguments are on a single line, the formatter leaves them as they are (unless they violate max line length), but inserting at least one newline, forces all into separate lines (same as it works currently for lists and operator chains):
unchanged:
a: b: c: a + b + c
in 1:
a:
b: c: a + b + c
out 1:
a:
b:
c:
a + b + c
in 2:
{
a,
x
}:
b: c: (a + b + c) * x
out 2:
{
a,
x,
}:
b:
c:
(a + b + c) * x
Then you could allow newlines, which would make the formatter "reset" the arguent formatting style (consitency only enforce per newline separated block), so the following would remain unchanged:
{
a,
x,
}:
b: c: (a + b + c) * x
a:
x:
b: c: (a + b + c) * x
a: x:
b:
c:
(a + b + c) * x