hindent
hindent copied to clipboard
Support custom formatting for <$> <*> combinations
In this example with hindent 5.1.0, my Opts
builder gets auto-formatted into something that IMO is more confusing:
opts :: Options.Parser Opts
opts =
Opts <$> argument str (metavar "MP3") <*>
strOption
(long "bucket" <> metavar "BUCKET" <> help "What bucket?" <> value defaultBucket) <*>
strOption
(long "imagePath" <> metavar "IMAGE" <>
help "What image to attach to the podcast" <>
value defaultImage) <*>
option
auto
(long "audioScale" <> metavar "IMAGE" <>
help "What image to attach to the podcast" <>
value defaultScale) <*>
strOption
(long "albumAuthor" <> metavar "ALBUM AUTHOR" <>
help "What image to attach to the podcast" <>
value defaultAuthor) <*>
(optional $
strOption
(long "setDate" <> metavar "YYYY-MM-DD" <> help "Date prefix override"))
I've gotten used to the more regular way of formatting operators at the end of the line, but I'm fine with adding specific support for these operators to be formatted something like this:
opts :: Options.Parser Opts
opts =
Opts <$> argument str (metavar "MP3")
<*> strOption
( long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket )
<*> strOption
( long "imagePath"
<> metavar "IMAGE"
<> help "What image to attach to the podcast"
<> value defaultImage )
<*> option auto
( long "audioScale"
<> metavar "IMAGE"
<> help "What image to attach to the podcast"
<> value defaultScale )
<*> strOption
( long "albumAuthor"
<> metavar "ALBUM AUTHOR"
<> help "What image to attach to the podcast"
<> value defaultAuthor )
<*> ( optional $ strOption
( long "setDate"
<> metavar "YYYY-MM-DD"
<> help "Date prefix override" ) )
As <$>
and <*>
are essentially another form of function application, I think it makes intuitive sense to format it as suggested. If you remove those operators, you should get the same formatting, with just a little less horizontal movement.
Good point regarding the horizontal movement. The suggested above one causes dependency on the length of the name Opts
which isn't ideal. Can anyone suggest an alternative layout?
Perhaps:
opts :: Options.Parser Opts
opts =
Opts
<$> argument str (metavar "MP3")
<*> strOption
( long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket )
<*> strOption
( long "imagePath"
<> metavar "IMAGE"
<> help "What image to attach to the podcast"
<> value defaultImage )
<*> option auto
( long "audioScale"
<> metavar "IMAGE"
<> help "What image to attach to the podcast"
<> value defaultScale )
<*> strOption
( long "albumAuthor"
<> metavar "ALBUM AUTHOR"
<> help "What image to attach to the podcast"
<> value defaultAuthor )
<*> ( optional $ strOption
( long "setDate"
<> metavar "YYYY-MM-DD"
<> help "Date prefix override" ) )
This looks great @chrisdone! :dancer:
I run into this problem with (&)
. Example input:
instance BinaryBit.BinaryBit Int32 where
getBits _ = do
bytes <- BinaryBit.getByteString 4
bytes
& LazyBytes.fromStrict
& Endian.reverseBitsInLazyBytes
& Binary.runGet Binary.get
& pure
And hindent
's output:
instance BinaryBit.BinaryBit Int32 where
getBits _ = do
bytes <- BinaryBit.getByteString 4
bytes & LazyBytes.fromStrict & Endian.reverseBitsInLazyBytes &
Binary.runGet Binary.get &
pure
It feels gross to add a bunch of special cases for certain operators. I don't have any better ideas, though.
Edited to add: Mostly because you could replace &
with $
or .
or >>>
or <<<
. And that's just from the base package. You could be using |>
or <|
instead.
Another solution is to always break before the operator on overlong lines. This avoids having to special case some set of operators.
Is the decision to break after operators arbitrary or is there a reason?
myProduct =
somethingLong
+ somethingElse
+ anotherThing
myApplicative =
Opts
<$> argument str (metavar "MP3")
<*> strOptions some more stuff
I like that approach. I prefer some indentation of the operators since it's required with do
notation.
How would it work with parentheses? Using Chris's example from above, I see a few options:
<*> strOption (long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket)
<*> strOption (
long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket)
<*> strOption
( long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket
)
My preference is for the third option
Opts
<$> strOption
( long "bucket"
<> metavar "BUCKET"
<> help "What bucket?"
<> value defaultBucket
)
+1. I'm fine with special-casing these by default, but having to put them in the config is fine too.
I still get output from hindent
that looks like this:
arbitraryXPrvKey :: Network -> Gen XPrvKey
arbitraryXPrvKey net =
XPrvKey <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitraryHash256 <*>
arbitrary <*>
pure net
I could live with it if it looked like this:
arbitraryXPrvKey :: Network -> Gen XPrvKey
arbitraryXPrvKey net =
XPrvKey
<$> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitraryHash256
<*> arbitrary
<*> pure net