Dante: Emacs mode for Interactive Haskell
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
Feature | Command | Keybinding |
---|---|---|
Flycheck type checking | flycheck-mode (†) | |
Company completion | company-mode | |
Type of selection | dante-type-at | C-c . |
Info at point | dante-info | C-c , |
Apply Ghc suggestion for error at point | attrap-attrap | C-c / |
Goto definition | xref-find-definitions | M-. |
Find uses | xref-find-references | M-? |
REPLoid (∗) | dante-eval-block | C-c ” |
Restart | dante-restart | |
Diagnosis | dante-diagnose | |
Remote operation over ssh, via tramp | N/A | N/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:
- 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. - 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
andxref-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.