Skip to content

IDE VS Code Haskell Extension (Haskell Language Server) ghcid

theGhostJW edited this page Oct 12, 2021 · 13 revisions

Why VSCode

  • I am not a keyboard warrior
  • defected from atom - slow an had serious UX issues when used with HIE
  • Notably VSCode has:
    • fast reloading
    • can cut paste type signatures from flyover hints
    • really polished UX - e.g. Ctrl-Shft-P

Haskell Extension

Note

Setting up the cradle file used by the Haskell extension can be tricky. The easiest way to do this is run the implicit-hie utility:

  1. Build your project using stack build
  2. Run implicit-hie

ghcid

  • ghcid used in a terminal separate from VSCode

stack

  • Fall back to stack for very broken builds

Misc

How It Woks

In order to work smoothly with a code base you need three things:

  1. IDE services such as code highlighting error identification and code hints.
  2. Low friction code evaluation.
  3. A compiler fallback for really broken builds when other tools are not working.

These services are provided by Haskell Language Server, ghcid and Stack.

Set Up

  1. To set up the VSCode simply install the Haskell plugin.

  2. Open your project in the root directory in VSCode and open a separate terminal (Powershell) to run ghcid (see installation instructions at ghcid).

  3. Enter the following in the VSCode terminal or separate terminal window running on a second screen:

    ghcid --command "stack ghci --test" --allow-eval --clear --no-height-limit

You are now set up for VSCode to provide IDE services and ghcid to provide auto evaluation and type checking across libraries within the same package.

How it works

The Haskell Language Server (HLS) plugin works as expected out of the box but at the time of writing will not auto detect and update changes by the plugin across libraries within the same package. So, for example, if you change a type in the main library the test library within the same package can still reference the old type and no error will be shown in the IDE.

The language server or VSCode will have to be restarted for these changes to be shown.

ghcid monitors the project files and interprets the files every time there is a change. ghcid will detect errors caused by cross library changes and also re-evaluate. ghcid is also more resilient than hie so there will be times HLS will stop working and you can keep doing fixes using ghcid. Once all is fixed you can then restart the HLS server via the Haskell plugin command (find using Ctrl-Shift-P).

Notes on ghcid Evaluation

To Mark ghcid evaluation use a comment such as the following:

-- $> hookResultPretty
hookResultPretty :: [Text]
hookResultPretty = prettyPrintLogProtocol SimpleText <$> hookRunResult

See this blog post for details: ghcid evaluation.

Now every time a file in the package containing hookResultPretty is saved ghcid will recheck and reevaluate the expression. The result will printed to the console.

Note that this reevaluation will only occur when the file changed is in the same library as eval statement. If this change occurs in a different library in the package then the eval statements in this package will not be shown after recompilation. You can force reevaluation by making a change in the same library as the eval statement. If there is no change that needs to be made you can make a fake change such as adding and removing a space. This will be enough to invoke ghcid re-evaluation.

If you want to run tests just eval $> a call to the main function in your test project. When you are done you can remove the eval line or, if you will be coming back to it just disable it with a space $ > like below:

-- $ > hookResultPretty
hookResultPretty :: [Text]
hookResultPretty = prettyPrintLogProtocol SimpleText <$> hookRunResult

You are now set up for rapid feedback with regards to both compilation and evaluation.

Falling Back to Stack

When you start deleting or moving files and modules around or making drastic type changes, often a project will get into a state where Haskell Language Server and ghcid will stop working. In such situations you can fall back to stack by running the following in a terminal:

stack test --file-watch --fast --no-run-tests

This cycles far slower than ghcid and Haskell Language Server, so once all is fixed, restart both and resume using those.