dante icon indicating copy to clipboard operation
dante copied to clipboard

  • Dante: Emacs mode for Interactive Haskell

[[https://gitter.im/dante-mode/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge][https://badges.gitter.im/dante-mode/Lobby.svg]] [[https://melpa.org/#/dante][https://melpa.org/packages/dante-badge.svg]] [[https://stable.melpa.org/#/dante][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

| 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:

#+BEGIN_SRC Haskell

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

-- >>> intercalate " " example

#+END_SRC In the above file, if you invoke ~dante-eval-block~ on the line containing "intercalate", you'll get:

#+BEGIN_SRC haskell -- >>> intercalate " " example -- "This is an example of interactive evaluation"

#+END_SRC

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

#+BEGIN_SRC haskell -- >>> let foo = "foo"

-- >>> foo ++ "bar" -- "foobar"

#+END_SRC

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.

*** Tramp If programs such as ~nix-shell~ are not found on the remote host, you may need to ajust the tramp path. For example: #+BEGIN_SRC elisp (add-to-list 'tramp-remote-path 'tramp-own-remote-path) #+END_SRC ** Installation

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

#+BEGIN_SRC elisp (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)
)

#+END_SRC

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.

*** Using dante + hlint

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:

#+BEGIN_SRC elisp :config (flycheck-add-next-checker 'haskell-dante '(info . haskell-hlint)) #+END_SRC

** Configuration 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:

#+BEGIN_SRC elisp ((nil . ((dante-methods . (new-impure-nix))))) #+END_SRC

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 [[https://www.gnu.org/software/emacs/manual/html_node/emacs/File-Variables.html#File-Variables][file-]] and [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html][directory-local]] variables (as recommended above).

** Comparison with Intero

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~ and ~xref-find-references~.
  • With Dante, Flychecking 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 eldoc (but can show type signatures in the minibuffer by modifying the variable: dante-tap-type-time), nor Hoogle. ** Troubleshooting

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.

** In the "press"

  • [[http://h2.jaguarpaw.co.uk/posts/how-i-use-dante/][How I use dante]]