Initial commit
[haskell-blog.git] / posts / setup.md
1 ---
2 title: 📦 Intro
3 date: 2018-05-24
4 ---
5
6 I was fortunate enough to be accepted for the Haskell foundation for this year's Summer of Code, and the project I will be working on is with the [Haskell IDE Engine](https://github.com/haskell/haskell-ide-engine).
7 From the repository description, the Haskell IDE Engine (hie) is the engine for Haskell IDE integration.
8 Most interestingly, it acts as a server for the Language Server Protocol so it can provide rich Haskell support for any IDE or text editor that supports the protocol.
9 It can give diagnostics, refactor code, search document symbols and tons of other neat stuff.
10 I'll be working on a test framework that can simulate a session interacting with a client. [You can read more about it here.](https://summerofcode.withgoogle.com/projects/#6041896627994624)
11
12 ## haskell-ide-engine
13 The project kind of acts as the glue between LSP and the various coding tools in the Haskell ecosystem.
14 It has a plugin API that's used for integrating fan favourites such as `ghc-mod`, `hlint` and `HaRE`.
15
16 It's spread across multiple repositories:
17
18 1. **haskell-ide-engine** provides the I/O and communicates between the client and ghc-mod, as well as any plugins such as HaRE or brittany
19 2. **haskell-lsp** (and **haskell-lsp-types**) contain definitions for functions and types according to the [LSP specification](https://microsoft.github.io/language-server-protocol/specification)
20 3. **ghc-mod** is a separate tool that provides most of the analysis and diagnostics, but its tightly coupled with HIE.
21 4. **haskell-lsp-client** is a library for LSP clients that my mentor Alan pointed out, we plan to use it as a starting point for #5.
22 5. **haskell-lsp-test** will soon be the testing framework!
23
24
25 ## IRC
26 I've mostly been communicating in the `#haskell-ide-engine` room on freenode.
27 It's been a while since I've used IRC, but I got round to setting up and running an IRC bouncer on my server (ZNC).
28 Launching [Colloquy](http://colloquy.info) was a blast from the past, complete with pre-retina icons.
29 But as it turns out it's [still actively developed on GitHub!](https://github.com/colloquy/colloquy).
30 It's no longer distributed on the website, so I cloned the repository, created an archive with Xcode and then moved the `.app` to `/Applications`.
31
32 ## Blog
33
34 At the moment I'm using this makeshift bash script to blog:
35
36 ```bash
37 cat header.html > index.html
38 for md in $(ls -tr *.md); do
39   markdown $md >> index.html
40 done
41 cat footer.html >> index.html
42 ```
43
44 ## Setup
45
46 It took me a while to get the development environment set up.
47
48 I started off by using VSCode and [vscode-hie-server](//github.com/alanz/vscode-hie-server) for the client.
49 There was an issue on master that caused the LSP parser to fail with VSCode, so I created a [pull request](https://github.com/alanz/haskell-lsp/pull/81) for it.
50
51 But I'm mainly a Vim user, so I tried out [vim-lsc](//github.com/natebosch/vim-lsc) and then eventually settled for [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim) which seems to support more LSP features.
52 Here's my current `~/.vimrc` bindings for it
53
54 ```vim
55 let g:LanguageClient_serverCommands = {
56       'haskell': ['hie', '--lsp', '--debug', '-l', '/tmp/hie.log', '--vomit']
57       \ }
58
59 nnoremap <silent> K :call LanguageClient#textDocument_hover()<CR>
60 nnoremap <silent> gd :call LanguageClient#textDocument_definition()<CR>
61 nnoremap <silent> gr :call LanguageClient#textDocument_rename()<CR>
62 nnoremap <silent> ga :call LanguageClient#textDocument_codeAction()<CR>
63 nnoremap <silent> gs :call LanguageClient#textDocument_documentSymbol()<CR>
64 set completefunc=LanguageClient#complete
65 ```
66
67 At one point I ended up getting strange errors from HIE when running it on haskell-lsp and haskell-ide-engine (very meta):
68 ```
69 hie: <command line>: cannot satisfy -package-id HaRe-0.8.4.1-inplace: 
70     HaRe-0.8.4.1-inplace is unusable due to shadowed dependencies:
71       base-4.11.1.0 Strfnsk-StrtgyLb-5.0.1.0-e162e946 cabal-helper-0.8.0.3-inplace containers-0.5.11.0 directory-1.3.1.5 ghc-8.4.2 ghc-xctprnt-0.5.6.1-3f0c080b ghc-mod-core-5.9.0.0-inplace hslggr-1.2.10-e253fcf2 mnd-cntrl-1.0.2.3-90183ebd syb-0.7-652252ef syz-0.2.0.0-ae4391c5
72     (use -v for more information)
73 ```
74
75 After two days of repeated `rm -r ~/.stack .stack-work`, I learnt two important lessons when working with stack projects:
76
77 1. Double check with `ls -a` to make sure there are no `dist`, `dist-newstyle`, `.cabal.project.local` or `.ghc-environment`s lying around. These will fool `ghc-mod` into thinking into using cabal instead of stack.
78 2. You need to use a `hie` that was compiled with the same GHC version as your stack resolver. Turning on the hie wrapper in VSCode can automatically find it for you, otherwise you will need to specify it yourself.
79
80 But once I got hie working, it was amazing. Being able to rename variables with two keystrokes, apply quick-fixes straight fresh from `hlint` and jump to symbols with fuzzy finding all from vim was a glorious feeling.
81
82 Unfortunately this did not last for long as the second time I launched vim I got this [delightful bug](https://github.com/haskell/haskell-ide-engine/issues/562).
83 It's an issue that lies all the way within `ghc` and only affects macOS on 8.4.2.
84 It looks like it won't get fixed till the next 8.6 release either, which is due around August.
85 It only affects modules that use the PatternSynonyms language extension, which `haskell-ide-engine` uses.
86 I'm still trying various linker flags to see if there is a workaround, but for the meantime it means that I can't use `hie` on `hie` without swapping out the ghc-8.4.2 resolver for ghc-8.2.
87
88 ## Starter PRs
89
90 Here are some PRs so far:
91
92 - [Preventing hie from showing quickfixes for hlint suggestions with no possible refactorings](https://github.com/haskell/haskell-ide-engine/pull/548)
93 - [Fixes for](https://github.com/haskell/haskell-ide-engine/pull/525) [extraneous newlines](https://github.com/haskell/haskell-ide-engine/pull/563) being added with Brittany
94 - [Restructuring the response and cache architecture](https://github.com/haskell/haskell-ide-engine/pull/568) to allow for deferred IDE responses
95
96 The last one is still a work in progress, but here's a summary of how it came about and what's going down.
97
98 - When trying out `haskell-lsp-client`, the document symbols request returned an empty list when run immediately after starting `hie`, unless a delay was added so that `hie` had time to load the module.
99 - I tried submitting a PR to move the symbol request from the `IdeM` monad to the `IdeGhcM` monad
100 - These two monads determine what thread they run on: `IdeM` is for internal requests and stuff, but `IdeGhcM` is for anything that goes through `ghc-mod`, which may take some time to run
101 - This turned out to be a bad idea™ since putting it on `IdeGhcM` would cause the request to block whenever a large module was being compiled in the background. We want it to only wait for the module to load whenever there was no cache available, but serve the cache whenever possible.
102
103 I owe all of my thanks to `wz1000` for noticing this and pointing me in the right direction.
104 The agreed plan is this:
105
106 Change the function that returns cached modules, `getCachedModule` to return not just a `Maybe` but a more descriptive ADT:
107
108 ```haskell
109 data CachedModuleResult = ModuleLoading
110                         | ModuleFailed String
111                         | ModuleCached CachedModule IsStale
112 ```
113
114 Add a queue of actions that get executed whenever a module is finished loading:
115
116 ```haskell
117 data IdeState = IdeState
118   { moduleCache :: GhcModuleCache
119   -- | A queue of actions to be performed once a module is loaded
120   , actionQueue :: Map.Map FilePath [CachedModule -> IdeM ()]
121   ...
122   }
123 ```
124
125 Change `IdeResponse` to be a full blown ADT instead of a pattern, and add a new deferred type that the dispatcher can distinguish between and handle the queueing for:
126
127 ```haskell
128 -- | The IDE response, with the type of response it contains
129 data IdeResponse a = IdeResponseOk a
130                    | IdeResponseDeferred FilePath (CachedModule -> IdeGhcM (IdeResponse a))
131                    | IdeResponseFail IdeError
132 ```
133
134 This is where I'm currently at.
135 My head hurts and this is turning out to be a huge undertaking for such a small edge case.
136
137 But as `wz1000` said:
138
139 > there really is no better way to get to know a codebase than to tear it up
140
141 Tomorrow the community bonding period ends, and the real coding begins.
142 I've a lot to learn about pracitcal Haskell: I've lived a very sheltered life in university, far away from the IO monad.
143 But I'm looking forward to working with my mentor Alan and the other lovely members of the Haskell community, and hopefully making the future of Haskell tooling a little better.