/dante

Primary LanguageEmacs Lisp

Dante: Emacs mode for Interactive Haskell

https://badges.gitter.im/dante-mode/Lobby.svg https://melpa.org/packages/dante-badge.svg https://stable.melpa.org/packages/dante-badge.svg

Dante is a fork of Intero mode which aims exclusively at providing a convenient frontend to GHCi. It steals good ideas from Intero, but it aims for light weightedness (see below for a detailed comparison).

Features

FeatureCommandKeybinding
Flycheck type checkingflycheck-mode (†)
Company completioncompany-mode
Type of selectiondante-type-atC-c .
Info at pointdante-infoC-c ,
Apply Ghc suggestion for error at pointattrap-attrapC-c /
Goto definitionxref-find-definitionsM-.
Find usesxref-find-referencesM-?
REPLoid (∗)dante-eval-blockC-c ”
Restartdante-restart
Diagnosisdante-diagnose
Remote operation over ssh, via trampN/AN/A

REPLoid (∗)

You can evaluate code by writing it in a comment of the form -- >>> and run dante-eval-block.

Example:

example :: [String]
example = ["This is an example", "of", "interactive", "evaluation"]

-- >>> intercalate " " example

In the above file, if you invoke dante-eval-block on the line containing “intercalate”, you’ll get:

-- >>> intercalate " " example
-- "This is an example of interactive evaluation"
--

Several commands in the same block will be executed in at once, so you can have local let statements.

-- >>> let foo = "foo"
--
-- >>> foo ++ "bar"
-- "foobar"
---

Any GHCi command can be put in such a block, but note however that:

  1. There is no guarantee that state will be maintained across several calls to dante-eval-block. In fact, Dante liberally calls :r and :l, and (re)sets various GHCi options.
  2. It is not supported to load and/or unload modules in such blocks, or set unexpected options. This may work, or may mess with Dante internals.

So if your goal is run your webserver/database/etc. within GHCi, you should not do it using dante.

Installation

Turn on Dante in your haskell-mode-hook. I recommend:

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init
  (add-hook 'haskell-mode-hook 'dante-mode)
  
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  ;; OR:
  ;; (add-hook 'haskell-mode-hook 'flymake-mode)
  )

(†) Dante can only check saved buffers. Therefore, it will save the current buffer every time that a check is triggered. To avoid any surprise (and a bug where GHCi does not see file changes), you may want to disable checks on idle after change:

(setq flymake-no-changes-timeout nil)
(setq flymake-start-syntax-check-on-newline nil)
(setq flycheck-check-syntax-automatically '(save mode-enabled))

You may additionally want to save buffer after some idle time after a change. With Emacs 26:

(auto-save-visited-mode 1)
(setq auto-save-visited-interval 1)

Using dante + hlint

You can activate the hlint checker in addition to the dante checker as follows:

(add-hook 'dante-mode-hook
   '(lambda () (flycheck-add-next-checker 'haskell-dante
                '(warning . haskell-hlint))))

Customization

Customization can be important to make sure that GHCi is properly loaded by dante, notably the variables dante-project-root and dante-repl-command-line. Use M-x customize-group dante to read the documentation. Note in particular that customization can be done on a per-file or per-project basis by using file- and directory-local variables.

Another useful variable to customize is dante-load-flags

Comparison with Intero

To the best of my knowledge, here is how Dante compares with Intero:

  • Dante has no dependency on “Stack”
  • Dante’s Emacs code is about half as long as that of Intero.
  • Dante does not depend on custom Haskell code, contrary to Intero. Thus, it will work if (and only if) GHCi works for your project. (Including via “Stack”.)
  • Dante supports xref-find-definitions and xref-find-references.
  • With Dante, Flychecking is optional (yet recommended), whereas Intero demands that you flycheck your code.
  • Dante has has a different approach to Haskell evaluation
  • Dante offers no support for eldoc, nor Hoogle.