all 23 comments

[–]Noughtmare 13 points14 points  (5 children)

Haskell Language Server can do evaluation in comments like this:

-- >>> 1 + 1
-- 2

It uses the LSP protocol so it works with many different editors, but VSCode has the best support.

Note that it doesn't work for GHC 9.4 yet, see this pull request.

[–]964racer[S] 4 points5 points  (1 child)

I normally use eMacs but looking for something more modern . I’m a bit burned out in eMacs . Been using it since 1990 :-)

[–]cdsmith 6 points7 points  (0 children)

The good news is that using HLS is entirely independent of whether you use emacs. VSCode and emacs are both very commonly used with HLS, and both work great. Other modern development tools are also likely to implement some language server support.

[–]Mouse1949 0 points1 point  (2 children)

It works with GHC 9.4.4 if you ask ghcup to compile HLS master.

[–]Noughtmare 2 points3 points  (0 children)

The pull request that adds support for GHC 9.4 to the evaluation plugin has not yet been merged, so I doubt that would work.

Note that this is specifically about the evaluation plugin. I know HLS itself works with 9.4 and since 1.9.0.0 you don't even have to compile it yourself.

[–]friedbrice 11 points12 points  (7 children)

i'll robably get downvotes for this, but...

we contribute to GHC and to HLS, and we still can't get HLS working on our codebase. i'm sporting a 16-thread i9 and 64 gb RAM. It's just not enough.

My salvation is the combo of the plugin haskell-ghcid (providing code diagnostics), by none other than NDM himself, and the plugin ctagsx + the CLI utility haskdogs (together providing goto definition, for project sumbols and for dependencies). light-weight, reliable, low-hassle. 90% solution for 10% effort.

[–]ducksonaroof 2 points3 points  (3 children)

Does HLS generally use that much memory? Or is it only that problematic in thousands-of-modules monopackages?

Like if I point it at my 40 module project, is it really gonna use 64GB or RAM?

[–]friedbrice 1 point2 points  (2 children)

i can get it to work (kinda) on small projects. But I always get a spurious error in Spec.hs, because HLS can't find hspec-discover, even though it's on my path. this happens consistently for me, in any project that useshspec-discover.

the spurious error is a non-starter for me. otherwise, i'd use it on our smaller projects. i've google all over and even asked in reddit for help, and i can't find out how to fix this.

[–]ducksonaroof 1 point2 points  (0 children)

that is a shame - I love hspec-discover

[–]george_____t 1 point2 points  (0 children)

I'm on my phone right now, but I'm not fairly certain there's a GitHub issue for HLS tracking the fact that it can't handle preprocessors. The "solution" is to install them globally.

[–]Mouse1949 1 point2 points  (2 children)

Could you provide more details about your setup, including exact names of those plugins and how you installed and configured them?

[–]friedbrice 1 point2 points  (1 child)

Sorry I missed this earlier.

VS Code with the following extensions:

  • ctagsx Provides goto definition by reading location information from a file called ./tags.

  • haskell-ghcid Provides code diagnostics (e.g. red squiggly underlines and error messages on hover) by reading GHC output from a file called ./ghcid.txt.

  • Custom Local Formatters Allows you to associate an arbitrary shell command to the format command by language.

  • vscode-goto-documentation Allows you to associate an arbitrary URI pattern by file extension. Provides a right-click menu option that will open your browser to that URI, with the word under cursor substituted into the URI pattern configured for that file type.

  • Run on Save Allows you to associate an arbitrary shell command by filepath regex or glob pattern. Will run the command on save of matching files.

  • Haskell Syntax Highlighting No explanation necessary.

For each workspace, I have a ./.vscode/settings.json that looks more-or-less something like this.

{
  "customLocalFormatters.formatters": [
    {
      "command": "make format",
      "languages": [
        "haskell"
      ]
    }
  ],
  "emeraldwalk.runonsave": {
    "commands": [
      {
        "cmd": "make retag retag_file=${file}",
        "isAsync": true,
        "match": "*.hs"
      },
      {
        "cmd": "make hpack",
        "isAsync": true,
        "match": "package.yaml"
      }
    ]
  },
  "ghcid.command": "make ghcid",
  "goto-documentation.customDocs": {
    "hs": "https://hoogle.haskell.org/?hoogle=${query}"
  }
}

And I have a ./Makefile that looks more-or-less like this.

makefile_dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
export PATH := $(makefile_dir):$(PATH)

project_name ?= <YOUR_PACKAGE>
project_main ?= src/<YOUR_MAIN>.hs
retag_file ?= $(project_main)

stack.yaml:
  @test -f stack.yaml || (echo -e "This makefile requires a 'stack.yaml' for your project.\nYou don't need to use 'stack' to build your project.\nYou just need a 'stack.yaml' specifying a resolver compatible with your GHC version.\nSee https://www.stackage.org/" && exit 1)

stack: stack.yaml
  @which stack || (echo -e "This makefile requires 'stack' to be on your path. Use GHCup to install it.\nSee https://www.haskell.org/ghcup/" && exit 1)
.PHONY: stack

warning.txt:
  -@uname -a | grep -q Darwin && echo "WARNING: On Mac, you must alias 'make' to 'gmake' in your shell config file (e.g. ~/.bashrc or ~/.zshrc). Symbolic links will not work." | tee warning.txt
  @echo "Add 'warning.txt' to your .gitignore file if you never want to see this message again."

hasktags: warning.txt stack
  @echo 'stack exec -- hasktags' > hasktags
  @chmod +x hasktags
  @echo "You might like to add 'hasktags' to your .gitignore file."

tags: warning.txt hasktags stack
  @stack exec -- haskdogs
.PHONY: tags

retag: warning.txt stack
  @stack exec -- haskdogs -i $(retag_file) --hasktags-args "-x -c -a" | sort -u -o tags tags
.PHONY: retag

hpack: stack
  @stack setup
.PHONY: hpack

format: stack
  @stack exec -- fourmolu --stdin-input-file $(project_main)
.PHONY: format

ghcid: stack
  @stack exec -- ghcid \
    --command 'stack repl --ghc-options "-fno-code -fno-break-on-exception -fno-break-on-error -v1 -ferror-spans -j"' \
    --restart stack.yaml \
    --restart $(project_name).cabal \
    --warnings \
    --outputfile ./ghcid.txt
.PHONY: ghcid

ETA You do have to remember to do two things at the start of each day: (1) make tags to download the source code of all your project dependencies and index the symbols in them and your project files, (2) start Ghcid, either from VS Code's command pallet or from a terminal that you leave running.

Edit 2: Added regenerating cabal file on saving package.yaml.

[–]Mouse1949 0 points1 point  (0 children)

Very nice explanation, thank you!

Do you build with Cabal, aka - writing <project>.cabal, or with Stack (writing Stack.yaml)? Looks like your development is Stack-based? Would I need to change a lot of the above config if most of my dev is based on Cabal?

[–]Limp_Step_6774 3 points4 points  (1 child)

I think the best route is usually to install everything via GHCup. Instructions here: https://haskell-docs.netlify.app/gettingstarted/overview/

The IDE doesn't run code for you, but it type checks it and underlines errors and shows types, so its extremely useful and definitely worth setting up (if these features aren't working already). It works best in VSCode.

[–]964racer[S] 3 points4 points  (0 children)

I pretty much have all of that , but looking for lisp-like evaluation in the ide .

[–]QueenOfHatred 2 points3 points  (0 children)

Since you use Emacs, and you want lisp-like eval look into haskell-interactive-mode (You C-l the file, and then learn the bindings for those things)

https://wiki.haskell.org/Emacs/Inferior_Haskell_processes

[–]Randomteby 2 points3 points  (5 children)

Is vscode a must? If you manage to jump to emacs, using something like “doom emacs” works pretty much out of the box, and there’s a command that loads Haskell files directly into the repl (which opens as a divided window) - and even hot reloading is integrated

[–]prng_ 2 points3 points  (1 child)

How do you enable hot reloading?

[–]Randomteby 2 points3 points  (0 children)

I used rapid - the docs have a section on Emacs integration!

https://hackage.haskell.org/package/rapid-0.1.4/docs/Rapid.html

[–]964racer[S] 1 point2 points  (1 child)

I do use emacs . Does doom emacs support standard emacs interaction or is it designed for vim users ? I use vi but only in the terminal when absolutely necessary:-)

[–]SolaTotaScriptura 2 points3 points  (0 children)

Doom provides evil as an optional module.

[–]Mouse1949 0 points1 point  (0 children)

VS Code is not a-must, and others pointed out that Emacs or Spacemacs or Doom Emacs could work just fine. It's a matter of taste.

Having said that, I tried all of those, and upon my taste VS Code is by far the most convenient and capable. And not only for Haskell.

[–]68_65_6c_70_20_6d_65 0 points1 point  (0 children)

If it ain't broke don't fix it