Default to the same extensions as GHC
The following function is part of a module.
type Trio = (Maybe Integer, String, String)
parse :: String -> Trio
parse text = (n, op, s)
where
pattern = "[-]?[[:digit:]]+"
(prefix, num, suffix) = text =~ pattern :: (String, String, String)
trim = T.unpack . T.strip . T.pack
n = R.readMaybe $ trim num
op = trim prefix
s = trim suffix
This compiles and works for the given test cases, but hlint emits an error.
Error: Parse error: on input `='
Found:
parse text = (n, op, s)
where
> pattern = "[-]?[[:digit:]]+"
(prefix, num, suffix) = text =~ pattern :: (String, String, String)
trim = T.unpack . T.strip . T.pack
I've gone through most of the tickets that reported similar errors, and the suggestion is to disable TemplateHaskell using the pragma {-# LANGUAGE NoTemplateHaskell #-}. However, that doesn't help in this case.
I can confirm that using the pragma {-# LANGUAGE NoPatternSynonyms #-} fixes the error. Can hlint not turn on language extensions magically?
@asarkar HLint could do anything it wanted. But it's a balance - do we break more users by turning on PatternSynonyms, or fix more users? I think the view is that it fixes more users than it breaks, so on by default is the right default. If you pass -XHaskell2020 or similar I think it restricts you to a specific language standard, so you can opt in to following a standard rather than the default behaviour.
What I don’t understand, perhaps because of my lack of experience working with Haskell, is that why hlint can’t use the user-provided configuration, if any, instead of imposing its own opinion about what’s good for the user?
@asarkar hlint will use the user provided configuration if it is given one. as @ndmitchell recommends pass -XGhc2021 (or -XHaskell2010 or -XHaskell98) to hlint. doing this you have precisely specified what base set of extensions are enabled.
GHC2021 is used by GHC if neither Haskell98 nor Haskell2010 is turned on explicitly.
It seems to be the default without me having to specify explicitly. That’s the way I’d like to keep it, so that the code is always compatible with the latest language specification. Notably, PatternSynonyms is not enabled by GHC2021.
GHC2021 is used by GHC if neither Haskell98 nor Haskell2010 is turned on explicitly.
It seems to be the default without me having to specify explicitly. That’s the way I’d like to keep it, so that the code is always compatible with the latest language specification. Notably,
PatternSynonymsis not enabled by GHC2021.
if no language specifier is provided hlint constructs its own set of extensions to default enable. that set enables more extensions than GHC would default enable if not invoked with an explicit language specifier.
update: i confirm that PatternSynonyms is in that set.
the default behavior is chosen for hlint to parse as many files as possible (i.e. default behavior = not give a language specifier).
if no language specifier is provided hlint constructs its own set of extensions
... which is different from what GHC chooses, hence the problem. Why not choose the default the same as GHC?
hlint to parse as many files as possible
Is there any substantiated data that shows the difference between hlint chosen extensions and GHC chosen extensions? In other words, can it be shown that the if hlint chose the same default extensions as GHC, it'd fail to parse x% of files?
if no language specifier is provided hlint constructs its own set of extensions
... which is different from what GHC chooses, hence the problem. Why not choose the default the same as GHC?
i agree this is a reasonable proposition.
hlint to parse as many files as possible
Is there any substantiated data that shows the difference between hlint chosen extensions and GHC chosen extensions? In other words, can it be shown that the if hlint chose the same default extensions as GHC, it'd fail to parse x% of files?
i don't know about that.
Now that we have established this ticket has a valid point, I’ve updated the title to better suit the problem.
GHC gets extensions from .cabal files, on the command line, in stack.yaml files, and in the source file itself, plus those it defaults to. Of those, HLint can readily see those in the source file and which GHC defaults to, but not the others. That means that to "do the same as GHC" we can either try and find all the other sources (hard, maybe impossible) or just turn on most that don't break too much (easy). Note that where we can match GHC (e.g. through Haskell Language Server) that's what we do. If Cabal could run HLint, that would also be a place where it should match the right flags. But given just the command line hlint tool, there's no reasonable way to just match GHC.
turn on most
There’s a definitive list of the extensions enabled by default by GHC. What this ticket is asking is for hlint to use that list instead of making up its own. Is that what you referred to as hard, almost impossible?
No, the list of extensions enabled by GHC in your project is based on those defined in Cabal, Stack, command line flags and elsewhere. We can get the list of extensions enabled if you type ghc, but since that doesn't work in most projects, it's not that helpful. What we want is to approximate the extensions enabled in your project.
I’ve none defined. I think that’s the only use case we are concerned about here. When the user does specify, that’s a different use case and if I understand correctly, isn’t handled currently. Setting the default list same as GHC would fix the first use case, and not make the second worse. It seems to be an improvement.
I'm not sure what you mean. Consider the case that Steve has a project. In his .cabal file he has the extension PatternSynonyms enabled. Currently HLint works. If I follow the same default as GHC, it would not. Audrey has a project, he uses a custom build system like Bazel, and in herproject he has TemplateHaskell turned on in the .bazelconfig that applies to every project in her megarepo. Currently HLint works, but with the same default as GHC, it doesn't. My understanding based on talking to users is that if HLint defaulted to what GHC does (which you can do with a .hlint.yaml file if you want), then fewer projects would work out of the box.
In his .cabal file he has the extension PatternSynonyms enabled. Currently HLint works. If I follow the same default as GHC, it would not
If user has enabled any extensions, then hlint won't fallback to defaults. All I'm saying is that when the user hasn't enabled any extensions, GHC defaults and hlint defaults aren't the same.
I don't follow. In the .cabal file the user has enabled PatternSynonyms. What should HLint do?
If user has enabled any extensions, then hlint won't fallback to defaults
How do we detect if a user has enabled any extensions in their .cabal/stack/bazel projects?