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).
|Flycheck type checking||flycheck-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 /|
|REPLoid (∗)||dante-eval-block||C-c ”|
|Remote operation over ssh, via tramp||N/A||N/A|
You can evaluate code by writing it in a comment of the form
-- >>> and run
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
dante-eval-block. In fact, Dante liberally calls
: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.
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 can be important to make sure that GHCi is properly
loaded by dante, notably the variables
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
Another useful variable to customize is
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
- 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.