Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Commit fd927c5

Browse files
joshveratclemrewinfreyaymannadeempatrickt
committed
Add protip doc
Co-Authored-By: Timothy Clem <[email protected]> Co-Authored-By: Rick Winfrey <[email protected]> Co-Authored-By: Ayman Nadeem <[email protected]> Co-Authored-By: Patrick Thomson <[email protected]> Co-Authored-By: Rob Rix <[email protected]>
1 parent 9be9cb3 commit fd927c5

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

docs/💡ProTip!.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# 💡ProTip!
2+
3+
## Performance
4+
5+
- You can conveniently build and run `semantic` with profiling enabled using `script/profile`. Any arguments you pass to the script will be passed along to `semantic`. It will archive the results in the `profiles` folder, and open the run when it’s finished.
6+
- Similarly, you can generate [threadscope](https://wiki.haskell.org/ThreadScope) profiles with `script/threadscope`.
7+
- To profile you will need to make sure profiteur is installed: `stack install profiteur`
8+
along with `ps2pdf`: `brew install ghostscript`.
9+
- The Haskell wiki has [some handy rules of thumb for strict evaluation](https://wiki.haskell.org/Performance/Strictness#Rule_of_Thumb_for_Strictness_Annotation). This can be useful when dealing with performance issues caused by long chains of lazy computations, and when ensuring sequencing (e.g. for parallelism or correct handling of FFI allocations).
10+
11+
12+
## Memory Leaks
13+
14+
Space leaks are sometimes introduced into Semantic when holding on to a reference for too long, building up unevaluated expressions, or by keeping around unneeded references. While GHC has a garbage collector that can prevent dangling pointers, it's still possible to create space leaks in connection with lazy evaluation. Space leaks not only consume more memory, but also slow down the garbage collector considerably!
15+
16+
Space leaks can be detected by running `semantic` with a restricted heap size. Neil Mitchell's blog post describes a method for [detecting space leaks](http://neilmitchell.blogspot.co.uk/2015/09/detecting-space-leaks.html) by analyzing the stack traces of stack overflows when compiling with a restricted heap size.
17+
18+
19+
## Building
20+
21+
`stack build --fast` is a nice way to speed up local development (builds without optimizations).
22+
23+
24+
## Testing
25+
26+
`stack build --fast semantic:test` builds and runs the unit tests.
27+
28+
- Find out what all the possible test arguments are with `stack test --help`.
29+
- Focus in on a particular test or set of tests with `-m`/`--match`:
30+
31+
stack test --test-arguments="-m ruby"
32+
33+
- Use `--skip` to run everything but matching tests:
34+
35+
stack test --test-arguments="--skip ruby"
36+
37+
- It can take a while to run them over the whole project. Focus in on a particular module with `--test-arguments`:
38+
39+
stack test --test-arguments=src/Data/Range.hs
40+
41+
42+
## Difftool
43+
44+
`git` can be configured to open diffs in `semantic` using `git-difftool`:
45+
46+
1. Install semantic to the local bin path: `stack install :semantic`
47+
48+
2. Configure `semantic` as a difftool:
49+
50+
git config difftool.semantic.cmd 'semantic diff --patch "$LOCAL" "$REMOTE"'
51+
52+
3. Optionally, configure `semantic` as the default difftool:
53+
54+
git config diff.tool semantic
55+
56+
4. Perform git diffs using semantic by invoking `git-difftool`:
57+
58+
# if configured as default
59+
git difftool
60+
# otherwise
61+
git difftool -t semantic
62+
63+
5. _Bonus round!_ Optionally, configure `git-difftool` to never prompt:
64+
65+
git config difftool.prompt false
66+
67+
68+
## Editing
69+
70+
- 1. Install ghc-mod from the semantic directory by running:
71+
72+
`stack install ghc-mod`
73+
74+
- 2. You'll need the `ide-haskell` plugins for atom. You can install through apm:
75+
76+
       `apm install haskell-ghc-mod ide-haskell ide-haskell-cabal linter linter-ui-default`
77+
78+
# Ctags Support
79+
80+
You can enable ctags support for the project by installing [`codex`](https://github.com/aloiscochard/codex) to generate
81+
a ctags file. This is often useful because haskell-ide-engine's jump-to-definition feature can break during editing.
82+
83+
`stack install hasktags`
84+
`git clone https://github.com/aloiscochard/codex && cd codex && stack install codex`
85+
86+
To generate or update the tags file:
87+
88+
`codex update`
89+
90+
Symbol support is handled by the `symbols-view` package in atom or your friendly editor's ctags support. In atom you can use `cmd-alt-down` and `cmd-alt-up` to jump to a definition and jump back to your original position. If you're using a `vim` plugin, they're also bound to `ctrl-]` and `ctrl-t`. `cmd-shift-r` lists all tags in the project.
91+
92+
The `symbols-view` package only recognizes files named `tags` in the root of your project so you'll have to add a symlink:
93+
94+
`ln -s codex.tags tags`
95+
96+
Alternatively, you can replace `symbols-view` with `joshvera/tags-view` in your atom packages which adds support for jumping to absolute paths (if you want to navigate to a haskell dependency or base library in stackage) and recognizes `codex.tags` files.
97+
98+
`git clone https://github.com/joshvera/tags-view ~/.atom/packages/tags-view`
99+
100+
Then disable the `symbols-view` package.
101+
102+
103+
## Semantic documentation in Dash
104+
105+
You can generate a `semantic` docset and import it into Dash locally. To do so run the `script/haddock` first to ensure Haddock documentation is generated. Then run `script/docset`. This should generate `.docset/semantic.docset` in the `semantic` repo. The last step is to import the `semantic.docset` into Dash. Open dash, open preferences, select the 'Docsets' tab, click the `+` icon to add a new docset, and direct the file browser to `semantic/.docsets/semantic.docset`.
106+
107+
108+
## Working with grammar datatypes
109+
110+
`haskell-tree-sitter` includes some TemplateHaskell machinery to generate a datatype from a tree-sitter parser’s symbol table. You can generally guess the constructors of that type by turning the snake_case production names from the tree-sitter grammar into UpperCamelCase names, but you can also have the compiler dump the datatype out in full in the repl:
111+
112+
λ :info Language.JSON.Grammar.Grammar
113+
114+
_Voilà!_ You’re now looking at the source code for the datatype generated from the symbol table:
115+
116+
data Language.JSON.Grammar.Grammar
117+
= Language.JSON.Grammar.END
118+
| Language.JSON.Grammar.AnonLBrace
119+
| Language.JSON.Grammar.AnonComma
120+
| Language.JSON.Grammar.AnonRBrace
121+
| Language.JSON.Grammar.AnonColon
122+
| Language.JSON.Grammar.AnonLBracket
123+
| Language.JSON.Grammar.AnonRBracket
124+
| Language.JSON.Grammar.String
125+
| Language.JSON.Grammar.Number
126+
| Language.JSON.Grammar.True
127+
| Language.JSON.Grammar.False
128+
| Language.JSON.Grammar.Null
129+
| Language.JSON.Grammar.HiddenValue
130+
| Language.JSON.Grammar.Object
131+
| Language.JSON.Grammar.Pair
132+
| Language.JSON.Grammar.Array
133+
| Language.JSON.Grammar.AuxObjectRepeat1
134+
| Language.JSON.Grammar.AuxArrayRepeat1
135+
| Language.JSON.Grammar.ParseError
136+
-- Defined at src/Language/JSON/Grammar.hs:10:1
137+
instance Bounded Language.JSON.Grammar.Grammar
138+
-- Defined at src/Language/JSON/Grammar.hs:10:1
139+
instance Ord Language.JSON.Grammar.Grammar
140+
-- Defined at src/Language/JSON/Grammar.hs:10:1
141+
instance Eq Language.JSON.Grammar.Grammar
142+
-- Defined at src/Language/JSON/Grammar.hs:10:1
143+
instance Enum Language.JSON.Grammar.Grammar
144+
-- Defined at src/Language/JSON/Grammar.hs:10:1
145+
instance Show Language.JSON.Grammar.Grammar
146+
-- Defined at src/Language/JSON/Grammar.hs:10:1
147+
148+
149+
## GHCi
150+
151+
The Haskell interactive repl (GHCi) allows you to quickly typecheck your work and test out ideas interactively. It’s always worth having a repl open, but we’ve particularly tuned some workflows, e.g. semantic assignment development, for the repl.
152+
153+
[pretty-printing]: pretty-printing
154+
155+
156+
### Configuration
157+
158+
We configure `ghci` with defaults & macros for use with `semantic` via the [`.ghci` file][] at the project root, and you can further customize its behaviour via the `~/.ghci` file.
159+
160+
Full docs for ghci can be found in the [user’s guide][ghci user’s guide].
161+
162+
[ghci user’s guide]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html
163+
[`.ghci` file]: https://github.com/github/semantic/blob/master/.ghci
164+
165+
166+
### Managing history
167+
168+
`ghci` uses the `haskeline` package to perform the `readline`-like navigation, search, and management of history, as well as tab-completions. `haskeline` has [several user preferences][haskeline user preferences] and [custom key bindings][haskeline custom key bindings] which can be configured per-user via the `~/.haskeline` file. For example, these preferences cause `haskeline` to include only one instance of repeated commands in history, and to place no upper bound on the number of commands it will remember:
169+
170+
```
171+
historyDuplicates: IgnoreAll
172+
maxHistorySize: Nothing
173+
```
174+
175+
[haskeline user preferences]: http://trac.haskell.org/haskeline/wiki/UserPrefs
176+
[haskeline custom key bindings]: http://trac.haskell.org/haskeline/wiki/CustomKeyBindings
177+
178+
179+
### Pretty-printing
180+
181+
By default, GHCi prints the results of expressions using their `Show` instances, which can be particularly difficult to read for large recursive structures like `Term`s and `Diff`s. The project’s [`.ghci` file][] provides `:pretty` & `:no-pretty` macros which respectively enable & disable colourized, pretty-printed formatting of result values instead. These macros depend on the the `pretty-show` & `hscolour` packages.
182+
183+
Since `:reload`ing resets local bindings, the [`.ghci` file][] also provides a convenient `:r` macro which reloads and then immediately re-enables `:pretty`.
184+
185+
You can use `:pretty` & `:no-pretty` like so:
186+
187+
```
188+
λ :no-pretty
189+
λ Data.Range.Range <$> [1..3] <*> [4..6]
190+
[Range {start = 1, end = 4},Range {start = 1, end = 5},Range {start = 1, end = 6},Range {start = 2, end = 4},Range {start = 2, end = 5},Range {start = 2, end = 6},Range {start = 3, end = 4},Range {start = 3, end = 5},Range {start = 3, end = 6}]
191+
λ :pretty
192+
λ Data.Range.Range <$> [1..3] <*> [4..6]
193+
[ Range { start = 1 , end = 4 }
194+
, Range { start = 1 , end = 5 }
195+
, Range { start = 1 , end = 6 }
196+
, Range { start = 2 , end = 4 }
197+
, Range { start = 2 , end = 5 }
198+
, Range { start = 2 , end = 6 }
199+
, Range { start = 3 , end = 4 }
200+
, Range { start = 3 , end = 5 }
201+
, Range { start = 3 , end = 6 }
202+
]
203+
```
204+
205+
206+
### Working in Assignment
207+
208+
When working in assignment, some setup is required. This macro automates that by automatically importing the necessary modules and outputs an example command. If you provide the language you are working with as an optional parameter, the example command is formatted for that language's specific needs (parser, example file extension, etc.).
209+
210+
The macro is defined as:
211+
212+
```
213+
:{
214+
assignmentExample lang = case lang of
215+
"Python" -> mk "py" "python"
216+
"Go" -> mk "go" "go"
217+
"Ruby" -> mk "rb" "ruby"
218+
"JavaScript" -> mk "js" "typescript"
219+
"TypeScript" -> mk "ts" "typescript"
220+
"Haskell" -> mk "hs" "haskell"
221+
"Markdown" -> mk "md" "markdown"
222+
"JSON" -> mk "json" "json"
223+
_ -> mk "" ""
224+
where mk fileExtension parser = putStrLn ("example: fmap (() <$) . runTask . parse " ++ parser ++ "Parser =<< Semantic.Util.blob \"example." ++ fileExtension ++ "\"") >> return ("import Parsing.Parser\nimport Semantic.Task\nimport Semantic.Util")
225+
:}
226+
227+
:def assignment assignmentExample
228+
```
229+
230+
And is invoked in GHCi like:
231+
232+
```
233+
λ :assignment Python
234+
```
235+
236+
The output produces a one line expression assuming the syntax to assign is in a file named `example` with the relevant programming language extension:
237+
238+
```haskell
239+
quieterm <$> parseFile pythonParser "example.py"
240+
```
241+
242+
243+
### Inspecting TreeSitter ASTs
244+
245+
Inspecting the parse tree from TreeSitter can be helpful for debugging. In GHCi, the command below allows viewing the TreeSitter production name of each node in the TreeSitter AST:
246+
247+
```haskell
248+
import TreeSitter.Java
249+
fmap nodeSymbol <$> parseFile javaASTParser "example.java"
250+
```
251+
252+
253+
### Using Threadscope
254+
255+
Threadscope is a tool for profiling the multi-threaded performance of Haskell programs. It allows us to see how work is shared across processors and identify performance issues related to garbage collection or bottlenecks in our processes.
256+
257+
To install threadscope:
258+
259+
1. Download a prebuilt binary from https://github.com/haskell/ThreadScope/releases .
260+
2. `chmod a+x` the result of extracting the release.
261+
3. `brew install gtk+ gtk-mac-integration`.
262+
4. profit.

0 commit comments

Comments
 (0)