haskell-names
Name resolution library for Haskell
http://documentup.com/haskell-suite/haskell-names
Version on this page: | 0.4.1@rev:1 |
LTS Haskell 18.28: | 0.9.9 |
Stackage Nightly 2022-02-07: | 0.9.9 |
Latest on Hackage: | 0.9.9.1 |
haskell-names-0.4.1@sha256:d4674b37ad9690fec8dffd7311c6f27d6785a09914fbe1ceaa4103d05e73f741,14110
Module documentation for 0.4.1
- Language
- Language.Haskell
- Language.Haskell.Names
- Language.Haskell.Names.Annotated
- Language.Haskell.Names.Exports
- Language.Haskell.Names.GetBound
- Language.Haskell.Names.GlobalSymbolTable
- Language.Haskell.Names.Imports
- Language.Haskell.Names.Interfaces
- Language.Haskell.Names.LocalSymbolTable
- Language.Haskell.Names.ModuleSymbols
- Language.Haskell.Names.Open
- Language.Haskell.Names.SyntaxUtils
- Language.Haskell.Names
- Language.Haskell
haskell-names
haskell-names does name and module resolution for haskell-src-exts AST.
Namely, it can do the following:
- for a module, compute its interface, i.e. the set of entities exported by the module, together with their original names.
- for each name in the module, figure out what it refers to — whether it’s bound
locally (say, by a
where
clause) or globally (and then give its origin).
Installation
To install a released version:
- Install Cabal and cabal-install from the git repository (the
master
branch) cabal install haskell-names hs-gen-iface
If you’re building a development version, then you might also need to install development versions of haskell-src-exts, haskell-packages, and hse-cpp.
Module interfaces
hs-gen-iface
is a «compiler» that generates interfaces for Haskell modules.
An interface is a JSON file that lists all entities (types, classes, functions
etc.) exported by every module. For example, here are a couple of entries from
Prelude.names
:
[
{
"fixity": null,
"origin": {
"name": "map",
"module": "GHC.Base",
"package": "base-4.7.0.0"
},
"entity": "value"
},
{
"fixity": null,
"origin": {
"name": "IO",
"module": "GHC.Types",
"package": "ghc-prim-0.3.1.0"
},
"entity": "data"
},
...
]
As you see, each entity is annotated with the module and package where it was originally defined, and also with its fixity. Additionally, class methods, field selectors, and data constructors are annotated with the class or type they belong to.
Generating interfaces
Thanks to haskell-packages, hs-gen-iface
is fully integrated with Cabal. To
produce and install interface files, pass --haskell-suite -w hs-gen-iface
flags
to cabal install
, for instance
cabal install --haskell-suite -w hs-gen-iface mtl
This assumes that the hs-gen-iface
executable is in your PATH
. You can specify
the full path to hs-gen-iface
after -w
, too.
Core packages
haskell-names comes with the global package database populated with some core packages:
% hs-gen-iface pkg list --global
base-4.7.0.0
ghc-prim-0.3.1.0
integer-simple-0.1.1.0
Compiling core packages by hand
Suppose you need to compile any of the core packages by hand — for example, to get a different version than the one bundled with haskell-names.
Core packages, such as ghc-prim
, integer-simple
, array
, and base
, are
highly GHC-specific and need to be tweaked a bit before they can be processed by
haskell-names. Get our modified versions:
Note that Cabal’s new dependency solver won’t let you install ghc-prim
or base
easily. There are two ways to work around this:
-
Use the old solver:
cabal install --haskell-suite -w hs-gen-iface --solver=topdown
-
Invoke all the steps manually:
cabal configure --haskell-suite -w hs-gen-iface cabal build cabal install --only
Using interfaces
You can parse interface files directly, but a better idea is to use
Distribution.HaskellSuite.Packages
API (from haskell-packages), combined with
the package database NamesDB
defined in Language.Haskell.Modules.Interfaces
.
Name resolution
There are two approaches to name resolution.
A simpler one is provided by the annotateModule
function which annotates the
module with scoping information.
A more advanced interface is given by the Language.Haskell.Names.Open
module.
Its essence is described in the article Open your name resolution.
It is, however, very experimental.
Example
Let’s say you have a module and you want to find out whether it uses
Prelude.head
.
import Language.Haskell.Exts.Annotated
import Language.Haskell.Names
import Language.Haskell.Names.Interfaces
import Distribution.HaskellSuite
import Distribution.Simple.Compiler
import Data.Maybe
import Data.List
import Data.Proxy
import qualified Data.Set as Set
import qualified Data.Foldable as Foldable
import Text.Printf
import Control.Applicative
import Control.Monad
main = do
-- read the program's source from stdin
source <- getContents
let
-- parse the program (using haskell-src-exts)
ast = fromParseResult $
parseModuleWithMode defaultParseMode {parseFilename="stdin"} source
-- get all installed packages (user and global)
pkgs <-
(++) <$>
getInstalledPackages (Proxy :: Proxy NamesDB) UserPackageDB <*>
getInstalledPackages (Proxy :: Proxy NamesDB) GlobalPackageDB
headUsages <- evalNamesModuleT (findHeads ast) pkgs
forM_ headUsages $ \loc ->
printf "Prelude.head is used at %s\n" (prettyPrint $ srcInfoSpan loc)
when (null headUsages) $
printf "Congratulations! Your code doesn't use Prelude.head\n"
-- this is a computation in a ModuleT monad, because we need access to
-- modules' interfaces
findHeads :: Module SrcSpanInfo -> ModuleT Symbols IO [SrcSpanInfo]
findHeads ast = do
-- first of all, figure out the canonical name of "Prelude.head"
-- (hint: it's "GHC.List.head")
Symbols values _types <- fromMaybe (error "Prelude not found") <$> getModuleInfo "Prelude"
let
headOrigName =
fromMaybe (error "Prelude.head not found") $
listToMaybe
-- this looks a bit scary, but we're just walking through all
-- values defined in Prelude and looking for one with unqualified
-- name "head"
[ origName
| SymValue { sv_origName = origName@(OrigName _pkg (GName _mod "head")) } <- Set.toList values
]
-- annotate our ast with name binding information
annotatedAst <-
annotateModule
Haskell2010 -- base language
[] -- set of extensions
ast
let
-- get list of all annotations
annotations = Foldable.toList annotatedAst
-- look for headOrigName
headUsages = nub
[ location
| Scoped (GlobalValue valueInfo) location <- annotations
, sv_origName valueInfo == headOrigName
]
return headUsages
Example invocation
% ./find-heads
one = head [1]
^D
Prelude.head is used at stdin: (1:7) - (1:11)
% ./find-heads
import Prelude hiding (head)
import Data.Text
f = head (pack "foo")
^D
Congratulations! Your code doesn't use Prelude.head
API documentation
See haskell-names haddock documentation.
The two modules you need are:
- Language.Haskell.Names — exports the core functions and data types
- Language.Haskell.Names.Interfaces — lets you work with haskell-names interface files
Other modules are more experimental, less documented, and you probably don’t need them anyway.
Known issues
See the list of all issues.
- Because a non-trivial amount of packages are not designed to work with anything except GHC, hs-gen-iface currently pretends to be GHC. This is of course not acceptable — contributions here are welcome. (#32)
- haskell-names doesn’t perform validation yet. If a module is not valid Haskell, then the behaviour is undefined. See the issues marked as validation.
- Symbol fixities are not recorded (#1)
- Type variables are not resolved (#2)
- Arrows are not fully supported (#8)
- Type/data families and associated types are not fully supported (#25)
Maintainers
Roman Cheplyaka is the primary maintainer.
Adam Bergmark is the backup maintainer. Please get in touch with him if the primary maintainer cannot be reached.
Changes
Changes
Version 0.4.1
- Export types defined by top level data family declarations
- Update to haskell-src-exts 1.16
Version 0.4
Replace data-lens
with data-lens-light
Version 0.3.3.2
Remove the upper version bound on Cabal
Version 0.3.3.1
Update to work with haskell-src-exts 1.15
Version 0.3.3
- Expose
Language.Haskell.Names.ModuleSymbols.getTopDecls
- Define a
Monoid
instance forLocalSymbolTable.Table
- Support for parallel list comprehensions
Version 0.3.2.8
Introduce a lower dependency bound on tasty-golden
in the test suite
Version 0.3.2.7
type-eq
is fixed; depend on the new version
Version 0.3.2.6
Work around a regression in type-eq
Version 0.3.2.5
Relax pretty-show
version bound
Version 0.3.2.4
Make haskell-names build GHC 7.8
Version 0.3.2.3
Include interfaces for array
Version 0.3.2.2
Allow pretty-show-1.6.2
in the test suite
Version 0.3.2.1
Use pretty-show-1.6.1
in the test suite
Version 0.3.2
- Export
getDeclHead
fromLanguage.Haskell.Names.SyntaxUtils
- Annotate
QName
s in export lists - Update the bundled library interfaces (they were broken in earlier 0.3.* versions because of the interface format change)
Version 0.3.1
- Documentation improvements
- Add
rfoldMap
- Relax Cabal dependency constraint to include Cabal-1.14
Version 0.3
This release brings support for record puns and wildcards.
Interface file format changes
For field selectors, the new field constructors
is added. It contains a list
of constructors that contain that field.
API changes
- The
sv_constructors
field is added toSymSelector
- Add
Language.Haskell.Names.SyntaxUtils.stringToName
- The class
GetBound
is moved to a new module,Language.Haskell.Names.GetBound
. Its method,getBound
, now has a new argument, the global symbol table NameInfo
got two more constructors to annotate wildcards,RecPatWildcard
andRecExpWildcard
Scope
now has a new field of typeWcNames
, which can be accessed through thewcNames
lens. This is needed for record wildcards resolution.- Add field selectors to
GName
- Don’t export
GName
andOrigName
fromGlobalSymbolTable
Version 0.2.1
- Fix a bug where global symbols were annotated as local
- Make the code compile with aeson-0.6.2.0
Version 0.2
- Proper annotations for types and binding sites.
NameContext
now has a few new constructors. - Properly support n+k patterns
- Expose the
Alg
datatype for open name resolution - Expose the
HasOrigName
class
Version 0.1.2
Restore compatibility with Cabal 1.16
Version 0.1.1
Dummy release to force rebuild on hackage (now that haskell-src-exts 1.14 is released).