02
Jul 2017
On Computer Technology
NOTE: This post features a very hacky way to work around things which will likely never be merged into the ghcmod-vim project. It is just a place for me to document what has worked for myself in case I encounter the same issue again. This also assumes you are primarily using Stack for your Haskell projects.
Yesterday afternoon, I took some time to install haskell-vim-now. Among the list of features I saw on the README, what caught my attention was its ability to show the type of an expression in Haskell source code by typing ,ht
in normal mode. So I tried it on a Haskell file and I see the following text in red at the status line:
ghcmod#command#type: Cannot guess type
It was pretty devastating for me to see this. After all, the installation took closer to an hour than 10 minutes. This morning I decided to do more googling and figured out the reason, which is stated right here: https://github.com/DanielG/ghc-mod/wiki#most-common-stack-related-issue
It turns out that haskell-vim-now uses ghcmod-vim which in turn calls ghc-mod to retrieve the type of the expression under our vim cursor when we press ,ht
. But because I was using Stack, the GHC and the libraries I was using to compile the Haskell source code is different from the GHC that ghcmod-vim was using to infer the types.
Here’s the output I got:
$ ghc-mod --version
ghc-mod version 5.7.0.0 compiled by GHC 8.0.2
versus
$ stack exec ghc -- --version
The Glorious Glasgow Haskell Compilation System, version 7.10.3
Notice the difference in the GHC versions.
The workaround stated in https://github.com/DanielG/ghc-mod/wiki#most-common-stack-related-issue only works to a certain extent for myself (will be explained later). Below, I present a step by step solution to solve the problem.
To set things straight, the ghc-mod
invoked by the ghc-mod --version
command above is installed in ${HOME}/.local/bin
by haskell-vim-now. Other than the fact that it may be using a different GHC version from what your Haskell project uses, it may also be missing the libraries needed by your project. What we want is to
This assumes that your Haskell source code is inside its own Stack project (created by stack new
). If so, cd into the project’s directory and run:
stack install ghc-mod
This step is essentially the same as the workaround given in https://github.com/DanielG/ghc-mod/wiki#most-common-stack-related-issue
haskell-vim-now installs the ghcmod-vim plugin in ${HOME}/.vim/bundle/ghcmod-vim
. I figured out that at some point it has to invoke the ghc-mod command. We just need to change appropriate occurrences of ghc-mod
to stack exec ghc-mod
so that ghcmod-vim will use the ghc-mod we installed in Step 1. A little browsing around and git grep reveals the following points where code needs to be changed:
NOTE: The following required source code changes are based on commit 1d192d13d68ab59f9f46497a0909bf24a7b7dfff of ghcmod-vim. Things may have changed since the time of writing of this post. Hence for the best effect, please run the git grep -n "\['ghc-mod'"
command in ${HOME}/.vim/bundle/ghcmod-vim
to figure out where you need to change the code and use the files below as a reference.
Modify:
let l:cmd = ['ghc-mod', '--silent']
to
let l:cmd = ['stack', 'exec', '--', 'ghc-mod', '--silent']
And in fact this was sufficient to let ,ht
correctly infer the type of an expression, because this code is inside the ghcmod#build_command
vim function and is used by various parts of the code to build the actual ghc-mod
command.
Modify:
\ substitute(vimproc#system(['ghc-mod', '--silent', 'root']), '\n*$', '', '')
to:
\ substitute(vimproc#system(['stack', 'exec', '--', 'ghc-mod', '--silent', 'root']), '\n*$', '', '')
Point three: https://github.com/eagletmt/ghcmod-vim/blob/1d192d13d68ab59f9f46497a0909bf24a7b7dfff/autoload/ghcmod/util.vim#L109
Modify:
let l:ghcmod = vimproc#system(['ghc-mod','version'])
to:
let l:ghcmod = vimproc#system(['stack', 'exec', 'ghc-mod','version'])
This should be pretty obvious but it’s here for completeness.
What did not work for me in the workaround stated at https://github.com/DanielG/ghc-mod/wiki#most-common-stack-related-issue was having the ,ht
command work after running vim using stack exec
, like stack exec -- vim source-file.hs
. Contrary to what is stated, running vim like this does not add the directory containing the current Haskell project’s ghc-mod
binary (that we installed via stack install ghc-mod
in Step 1 of the solution) to the PATH
environment variable. As such, the ghc-mod
vim that’s invoked by the ghcmod-vim plugin is still the one installed in ${HOME}/.local/bin
.
Because there are a few layers of abstraction, you might want to check if using ghc-mod
directly on a Haskell source file actually works. Assuming you have already carried out step 1 of the solution above, simply run stack exec ghc-mod type source-file.hs row col
and see if there is meaningful output (of course replacing source-file.hs
, row
, col
with appropriate values).
For instance, given the following source file named hello.hs
:
main :: IO ()
main = putStrLn "Hello, World!"
To find out the type of putStrLn
, run:
stack exec ghc-mod type hello.hs 2 8
You should see the following output:
2 8 2 16 "String -> IO ()"
2 8 2 32 "IO ()"
2 1 2 32 "IO ()"
Disclaimer: Opinions expressed on this blog are solely my own and do not express the views or opinions of my employer(s), past or present.