As the new maintainer of haskell-names
I am happy to release version 0.5.0. The biggest
changes are:
- Class and instance declarations are now properly resolved
- Unify the two types for type-level and value-level symbols
- Annotate global symbol occurrences with their qualification
- Remove all the different notions of names
- Symbols do not contain package information anymore
All the names exported from a module are stored in a .names
file in JSON format. For example a small excerpt from Prelude.names
looks like:
[
{
"name": "map",
"entity": "value",
"module": "GHC.Base"
},
{
"name": "IO",
"entity": "newtype",
"module": "GHC.Types"
},
...
A symbol is uniquely identified by its name, the module it originates from and what entity it refers to. Some symbols carry additional information, for example constructors have an additional field for the type they belong to.
Let’s look at the new example from the haskell-names github page. The findHeads
function finds all occurrences of the head
symbol in a given AST of a Haskell module. It needs access to stored name information and therefore runs in ModuleT
.
findHeads :: Module SrcSpanInfo -> ModuleT [Symbol] IO [SrcSpanInfo]
findHeads ast = do
First we get all symbols exported from Prelude
with getModuleInfo
.
symbols <- fromMaybe (error "Prelude not found") <$>
getModuleInfo "Prelude"
Then we filter those for the one with name "head"
.
let
headSymbol =
fromMaybe (error "Prelude.head not found") (listToMaybe (do
symbol <- symbols
guard (symbolName symbol == UnAnn.Ident "head")
return symbol))
We annotate the given ast.
annotatedAst <-
annotateModule
Haskell2010 -- base language
[] -- set of extensions
ast
We get a list of all annotations from the annotated module.
let
annotations = Foldable.toList annotatedAst
A GlobalSymbol
annotation means that the annotated name refers to a global symbol. It also contains the qualified name that corresponds to how it is referenced but that is not needed here.
headUsages = nub (do
Scoped (GlobalSymbol globalSymbol _) location <- annotations
guard (globalSymbol == headSymbol)
return location)
And finally we return all found usages.
return headUsages
That concludes the brief example of how you could use haskell-names
.
Big thanks to Roman for his awesome work.