commercialhaskell/rio

Recommended set of language extensions?

snoyberg opened this issue · 15 comments

And should we advise setting these in the package.yaml file vs LANGUAGE pragmas.

I think I’m a minority opinion, but I always enable those I consider “safe” package-wide:

  • NoImplicitPrelude (with classy-prelude, classy-prelude-conduit, foundation etc)
  • OverloadedStrings
  • OverloadedLists
  • LambdaCase
  • GADTSyntax
  • RankNTypes
  • ScopedTypeVariables
  • DeriveGeneric
  • TupleSections
  • BangPatterns
  • MultiParamTypeClasses
  • FlexibleInstances
  • FlexibleContexts
  • MultiWayIf
  • TypeFamilies
  • TypeOperators
  • FunctionalDependencies
  • DisambiguateRecordFields
  • MonadComprehensions
  • BinaryLiterals
  • RecursiveDo
  • ParallelListComp
  • PartialTypeSignatures
  • RecordWildCards
  • PatternSynonyms
  • EmptyCase
  • InstanceSigs
  • KindSignatures
  • DeriveFunctor
  • DeriveFoldable
  • DeriveTraversable
  • ConstraintKinds
  • ExplicitNamespaces

Once there is a rio template on stack new, any selected choices could probably go immediately there, and a recommendation is to just stack new PACKAGE rio.

I like the idea of not writing many language pragmas as well.
Afterall, not having to manually add dependencies is very similar..

In addition to the question of which language extensions, is whether it's acceptable to put the language extensions in the package.yaml/cabal file instead of in each module. There's been tooling pushback on this in the past (I think from @nh2, but I'm not sure I'm remembering correctly).

nh2 commented

Yes that would be me.

haskell-source-exts (and tools built on it) and ghci do not understand cabal files.

If you put pragmas into cabal files, you cannot trivially use some of these tools (e.g. for generating simpe ctags, you only need to parse, which you can do per-file without having to implement any support for cabal), or quickly load a couple of different modules from different packages into one ghci by using the -i flag (because -X flags to ghci are global).

Without the haskell-source-exts programs I could live, or force them to implement support by depending on the Cabal package (and the extra maintenance effort to keep up with the changes in that), but the no-manual-ghci problem is a real killer.

nh2 commented

I guess I should also bring in some form of bold statement:

I would happily choose spending a few seconds at the creation of each .hs file (and forcing coworkers and community members to do so) over manually re-creating this work each time I have to do some ghci based surgery, and then not commit this work (of putting the pragmas in each file).

Then we can put something like this on the template, and provide for some editors a snippet for the few seconds mentioned:

{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, OverloadedLists,
  LambdaCase, GADTSyntax, RankNTypes, ScopedTypeVariables,
  DeriveGeneric, TupleSections, BangPatterns, MultiParamTypeClasses,
  FlexibleInstances, FlexibleContexts, MultiWayIf, TypeFamilies,
  TypeOperators, FunctionalDependencies, DisambiguateRecordFields,
  MonadComprehensions, BinaryLiterals, RecursiveDo, ParallelListComp,
  PartialTypeSignatures, RecordWildCards, PatternSynonyms, EmptyCase,
  InstanceSigs, KindSignatures, DeriveFunctor, DeriveFoldable,
  DeriveTraversable, ConstraintKinds, ExplicitNamespaces #-}

module SomeModule
  (
  ) where

import RIO
nh2 commented

Yeah I guess that'd do.

One might debate whether it should be like that or one-per-line which makes diffs look better, but for the purpose of ghci, that doens't matter.

We should also consider that some of these proposed ones might be controversial. For example, some consider RecordWildCards an antipattern that makes things less readable (though that is about the usage of the {..}, not of the language extension being enabled; it could still be enabled but one might recommend against using it in many cases), and OverloadedLists mght be too (it can make code compile that you don't want to compile).

I’d say aggregate for the “commonly agreed-upon subset” and one per line for others, then you have the most compact representation that is amenable to good workflow.
In theory there would never be a diff for the topmost value that defines what Haskell201X would.

Oops, I didn't have a watch set on this repo yet, so only just saw this issue. Until ghci can properly load multiple packages at once, we should not recommend any default extensions that are known to cause some code to no longer compile. There's a list here: https://github.com/commercialhaskell/stack/blob/864831f8f2d4c91e5989f2cbfbe3769c03946e0d/src/Stack/Ghci.hs#L635

So, this would mean removing OverloadedStrings, NoImplicitPrelude, and also GADTs and TypeFamilies (anything that implies MonoLocalBinds). Ugly I know, hopefully https://ghc.haskell.org/trac/ghc/ticket/10827 gets addressed sometime. Even addressing it only for extensions would be nice.

I guess the alternative approach would be to convert libraries to using these, but that's hard if you ever want to load older packages into your ghci along with newer.

Another reason to not include OverloadedStrings / OverloadedLists is that they break Paths_ modules, and hpack includes them automatically: commercialhaskell/stack#3789

akhra commented

For what it's worth, I've also run into stylish-haskell choking on files that use an extension's syntax without declaring it at the top (when invoked via vscode+Haskero, anyway).

Would a middle ground be to include in the stack new PACKAGE rio template, a .ghci which sets the same extensions that would be set in package.yaml/cabal? Similarly .stylish-haskell.yaml also supports setting default language extensions—or is it too much to assume people would setup projects using the stack templates?

I would personally hate to have that long list of extensions in the top, but I don't know if the other tooling, such as haskell-source-exts, would be able to use that?

nh2 commented

Would a middle ground be to include in the stack new PACKAGE rio template, a .ghci which sets the same extensions that would be set in package.yaml/cabal?

I don't think so, because .ghci files don't reliably work inside version control. E.g. git resets the permissions of it on checkout, and ghci refuses to use .ghci files if the permissions are not as it likes.

I think we're stuck with language pragmas in the modules themselves for now. The list is already documented, and in the future we can include that list in a template. Closing as complete.