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).
Feature | Command | Keybinding |
---|---|---|
On the fly type checking | flymake-mode, or 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 | (automatic) |
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.
If programs such as nix-shell
are not found on the remote host, you
may need to ajust the tramp path. For example:
(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
Dante has buitin eldoc support (showing info about the symbol at point in the modeline when idle.) Unfortunately, at the time of writing (Oct 2022), the standard Haskell mode uses the old eldoc API, overriding Dante’s eldoc support. I recommend just disabling the standard Haskell mode eldoc support, which IMO isn’t very helpful anyway, like so:
(add-hook 'haskell-mode-hook
(defun my-fix-hs-eldoc ()
(setq eldoc-documentation-strategy #'eldoc-documentation-default)))
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 'flycheck-mode)
;; OR for flymake support:
(add-hook 'haskell-mode-hook 'flymake-mode)
(remove-hook 'flymake-diagnostic-functions 'flymake-proc-legacy-flymake)
(add-hook 'haskell-mode-hook 'dante-mode)
)
If you want to see type of whatever is under the cursor at the moment as you move the cursor around, live, you can set `dante-tap-type-time` variable to number of seconds after which type information will appear in the minibuffer.
You can activate the hlint checker in addition to the dante checker by
calling flycheck-add-next-checker
appropriately, for example by
adding a config
section in the use-package dante
call:
:config
(flycheck-add-next-checker 'haskell-dante '(info . haskell-hlint))
Configuration can be important to make sure that GHCi is properly
loaded by dante. Even though Dante will do its best to figure out the
proper way to load GHCi for your project, it may still fail. A typical
way to configure GHCi command line is to a add a .dir-locals.el
file
to your project root, with the following contents:
((nil . ((dante-methods . (new-impure-nix)))))
Replace new-impure-nix
with the proper value, which you can figure
out by M-x describe-variable <RET> dante-methods-alist
.
For more fine-grained configuration, check dante-project-root
,
dante-repl-command-line
and dante-load-flags
. Use M-x
customize-group dante
to read the documentation for all customizable
variables. Note in particular that customization can be done on a
per-file, per-package or per-project basis by using file- and
directory-local variables (as recommended above).
To the best of my knowledge, Intero is no longer maintained. For posterity, 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
. - Dante has native eldoc support (Emacs 28)
- With Dante, on-the-fly checking is optional (yet recommended), whereas Intero demands that you flycheck your code.
- Dante has a different approach to Haskell evaluation
- Dante offers no support for Hoogle.
If dante-type-at
gives Couldn't guess that module name. Does it
exist?
or xref-find-definitions
gives ~No definitions found for:
“/tmp/danteTqJJvj.hs” , you may need to add your targets to
~.dir-locals.el
; see the Configuration section above.