Skip to content

Commit d41ae4c

Browse files
Philipp Schusterphischu
Philipp Schuster
authored andcommitted
Improve documentation, closes #92.
1 parent bfa7d17 commit d41ae4c

File tree

6 files changed

+75
-89
lines changed

6 files changed

+75
-89
lines changed

README.md

Lines changed: 29 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -60,94 +60,40 @@ Its essence is described in the article [Open your name resolution][openrec].
6060

6161
[openrec]: http://ro-che.info/articles/2013-03-04-open-name-resolution.html
6262

63-
### Example
64-
65-
Let's say you have a module and you want to find out whether it uses
66-
`Prelude.head`.
67-
68-
``` haskell
69-
module Main where
70-
71-
import Language.Haskell.Exts.Annotated (
72-
fromParseResult, parseModuleWithMode, defaultParseMode,
73-
parseFilename, prettyPrint, srcInfoSpan)
74-
import Language.Haskell.Exts (
75-
Name(Ident), ModuleName(ModuleName))
76-
import Language.Haskell.Names (
77-
loadBase, annotate, symbolName,
78-
Scoped(Scoped), NameInfo(GlobalSymbol))
79-
80-
import qualified Data.Map as Map (
81-
lookup)
82-
83-
import Data.Maybe (
84-
fromMaybe, listToMaybe)
85-
import Data.List (
86-
nub)
87-
import qualified Data.Foldable as Foldable (
88-
toList)
89-
import Control.Monad (
90-
forM_, guard)
91-
92-
main :: IO ()
93-
main = do
94-
95-
-- read the program's source from stdin
96-
source <- getContents
97-
98-
-- parse the program (using haskell-src-exts)
99-
let ast = fromParseResult (
100-
parseModuleWithMode defaultParseMode {parseFilename="stdin"} source)
101-
102-
-- get base environment
103-
baseEnvironment <- loadBase
104-
105-
-- get symbols defined in prelude
106-
let preludeSymbols = fromMaybe (error "Prelude not found") (
107-
Map.lookup (ModuleName "Prelude") baseEnvironment)
108-
109-
-- find a Prelude symbol with name 'head' using the List monad
110-
let headSymbol = fromMaybe (error "Prelude.head not found") (
111-
listToMaybe (do
112-
preludeSymbol <- preludeSymbols
113-
guard (symbolName preludeSymbol == Ident "head")
114-
return preludeSymbol))
115-
116-
-- annotate the AST
117-
let annotatedAST = annotate baseEnvironment ast
118-
119-
-- get all annotations
120-
let annotations = Foldable.toList annotatedAST
121-
122-
-- filter head Usages in List monad and remove duplicates
123-
let headUsages = nub (do
124-
Scoped (GlobalSymbol globalSymbol _) location <- annotations
125-
guard (globalSymbol == headSymbol)
126-
return location)
127-
128-
case headUsages of
129-
[] ->
130-
putStrLn "Congratulations! Your code doesn't use Prelude.head"
131-
_ -> forM_ headUsages (\location ->
132-
putStrLn ("Prelude.head is used at " ++ (prettyPrint (srcInfoSpan location))))
63+
### Examples
13364

134-
```
135-
136-
#### Example invocation
65+
The example in `examples/HeadUsage.hs` shows how you would find out if a
66+
Haskell modules given on stdin uses `Prelude.head`.
13767

68+
```
69+
% cabal exec -- runghc examples/HeadUsages.hs
70+
one = head [1]
71+
^D
72+
Prelude.head is used at stdin: (1:7) - (1:11)
73+
74+
% cabal exec -- runghc examples/HeadUsages.hs
75+
import Prelude hiding (head)
76+
import Data.Text
77+
78+
f = head (pack "foo")
79+
^D
80+
Congratulations! Your code doesn't use Prelude.head
81+
```
13882

139-
% ./find-heads
140-
one = head [1]
141-
^D
142-
Prelude.head is used at stdin: (1:7) - (1:11)
83+
The example in `examples/ModuleExports.hs` shows how the `resolve` function
84+
behaves. It expects to find `examples/moduleexports.Example.hs` and
85+
`examples/moduleexports/Example/Internal.hs`.
14386

144-
% ./find-heads
145-
import Prelude hiding (head)
146-
import Data.Text
87+
```
88+
% cabal exec -- runghc examples/ModuleExports.hs
89+
Only example: fromList [(ModuleName () "Example",[])]
90+
Only internal: fromList [(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
91+
Example & Internal: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
92+
Internal & Example: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
93+
Example after Internal: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
94+
Internal after Example: fromList [(ModuleName () "Example",[]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
95+
```
14796

148-
f = head (pack "foo")
149-
^D
150-
Congratulations! Your code doesn't use Prelude.head
15197

15298
### API documentation
15399

examples/HeadUsages.hs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
module Main where
22

3-
import Language.Haskell.Exts.Annotated (
4-
fromParseResult, parseModuleWithMode, defaultParseMode,
5-
parseFilename, prettyPrint, srcInfoSpan)
63
import Language.Haskell.Exts (
4+
fromParseResult, parseModuleWithMode, defaultParseMode,
5+
parseFilename, prettyPrint, srcInfoSpan,
76
Name(Ident), ModuleName(ModuleName))
87
import Language.Haskell.Names (
98
loadBase, annotate, symbolName,
@@ -36,13 +35,13 @@ main = do
3635

3736
-- get symbols defined in prelude
3837
let preludeSymbols = fromMaybe (error "Prelude not found") (
39-
Map.lookup (ModuleName "Prelude") baseEnvironment)
38+
Map.lookup (ModuleName () "Prelude") baseEnvironment)
4039

4140
-- find a Prelude symbol with name 'head' using the List monad
4241
let headSymbol = fromMaybe (error "Prelude.head not found") (
4342
listToMaybe (do
4443
preludeSymbol <- preludeSymbols
45-
guard (symbolName preludeSymbol == Ident "head")
44+
guard (symbolName preludeSymbol == Ident () "head")
4645
return preludeSymbol))
4746

4847
-- annotate the AST

examples/ModuleExports.hs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-- Taken from https://github.com/haskell-suite/haskell-names/issues/92
2+
module Main where
3+
4+
import Language.Haskell.Exts (ParseResult (ParseOk), parseFile)
5+
import Language.Haskell.Names (resolve)
6+
7+
main :: IO ()
8+
main = do
9+
ParseOk example <- parseFile "examples/moduleexports/Example.hs"
10+
ParseOk exampleInternal <- parseFile "examples/moduleexports/Example/Internal.hs"
11+
12+
let onlyExample = resolve [example] mempty
13+
let onlyInternal = resolve [exampleInternal] mempty
14+
let exampleAndInternal = resolve [example, exampleInternal] mempty
15+
let internalAndExample = resolve [exampleInternal, example] mempty
16+
let exampleAfterInternal = resolve [example] onlyInternal
17+
let internalAfterExample = resolve [exampleInternal] onlyExample
18+
19+
putStrLn $ "Only example: " ++ show onlyExample
20+
putStrLn $ "Only internal: " ++ show onlyInternal
21+
putStrLn $ "Example & Internal: " ++ show exampleAndInternal
22+
putStrLn $ "Internal & Example: " ++ show internalAndExample
23+
putStrLn $ "Example after Internal: " ++ show exampleAfterInternal
24+
putStrLn $ "Internal after Example: " ++ show internalAfterExample
25+
26+
{- Result:
27+
Only example: fromList [(ModuleName () "Example",[])]
28+
Only internal: fromList [(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
29+
Example & Internal: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
30+
Internal & Example: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
31+
Example after Internal: fromList [(ModuleName () "Example",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
32+
Internal after Example: fromList [(ModuleName () "Example",[]),(ModuleName () "Example.Internal",[Value {symbolModule = ModuleName () "Example.Internal", symbolName = Ident () "details"}])]
33+
-}

examples/moduleexports/Example.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module Example (details) where
2+
3+
import Example.Internal
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Example.Internal (details) where
2+
3+
details :: Int -> String
4+
details = undefined

src/Language/Haskell/Names/Recursive.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import Language.Haskell.Names.Annotated
2424

2525

2626
-- | Takes a list of modules and an environment and updates the environment
27-
-- with each of the given modules' exported symbols.
27+
-- with each of the given modules' exported symbols. The modules can appear
28+
-- in any order and can be mutually recursive.
2829
resolve :: (Data l, Eq l) => [Module l] -> Environment -> Environment
2930
resolve modules environment = updatedEnvironment where
3031
moduleSCCs = groupModules modules

0 commit comments

Comments
 (0)