haskell / haskell/haskell-mode

Extend haskell-build-type to look for .nix files

Open
#1,787 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Emacs Lisp
Stars
1.4k
Forks
354
Avg merge
5d 10h
Merged PRs (30d)
2

Description

This is not a bug but a missed edge case, the problem is:

1. I run nixos and use nix for haskell projects
2. because of (1) I _do not_ have cabal, stack or ghc globally installed
3. So I have `flake.nix` and `shell.nix` and friends in root directories of project.

Now the traditional way to run `inferior-haskell-process` is with `haskell-process-wrapper` like so (this uses the `nix-sandbox` emacs package):
```
(setq default-nix-wrapper
(lambda (args)
(append
(append (list "nix-shell" "--command")
(list (mapconcat 'identity args " ")))
(list (nix-current-sandbox))))

(setq haskell-process-wrapper-function
(lambda (args)
(apply 'nix-shell-command (nix-current-sandbox) args)))
```

However, this doesn't work because `haskell-build-type` in `haskell-customize.el` looks for a `cabal` `cabal.project` `stack` file _and_ an executable on path:
```
(defun haskell-build-type ()
"Looks for cabal and stack spec files.
When found, returns a pair (TAG . DIR)
where TAG is 'cabal-project, 'cabal-sandbox. 'cabal, or 'stack;
and DIR is the directory containing cabal or stack file.
When none found, DIR is nil, and TAG is 'ghc"
;; REVIEW maybe just 'cabal is enough.
(let ((cabal-project (locate-dominating-file default-directory "cabal.project"))
(cabal-sandbox (locate-dominating-file default-directory "cabal.sandbox.config"))
(stack (locate-dominating-file default-directory "stack.yaml"))
(cabal (locate-dominating-file
default-directory
(lambda (d)
(cl-find-if
(lambda (f) (string-match-p ".\\.cabal\\'" f))
(directory-files d))))))
(cond
((and cabal-project (executable-find "cabal"))
(cons 'cabal-project cabal-project))
((and cabal-sandbox (executable-find "cabal"))
(cons 'cabal-sandbox cabal-sandbox))
((and stack (executable-find "stack"))
(cons 'stack stack))
((and cabal (executable-find "cabal")) ;; <---------------------------------------------- right here
(cons 'cabal cabal))
((executable-find "ghc") (cons 'ghc nil))
(t (error "Could not find any installation of GHC.")))))
```

This means that when I try `run-haskell` or `haskell-process-load-file` I get `Could not find any installation of GHC` _even though_ `ghc` `cabal` and friends are accessible from my nix shell.

So to summarize:
1. the current implementation of `haskell-build-type` _assumes_ I have `stack` or `cabal` globally installed.
2. However, the user _could not_ have these globally installed and instead choose to provide them in a nix shell. In this scenario `haskell-build-type` does not realize this and prematurely errors out, even though if it ran inside the nix shell everything would be fine.

So I think there are two paths forward:
1. add `.nix` files to the `cond` expression in `haskell-build-type`, or
3. detect if `haskell-process-wrapper-function is set, if so run `executable-find "foo"` through that.

I think 2 likely makes more sense.

Contributor guide

Open the contributing guide

Research direction

Start in haskell-customize.el at haskell-build-type and review how its cabal, stack, and executable checks determine the build type. Compare that detection with the haskell-process-wrapper-function and the flake.nix or shell.nix setup described here. Done means a Nix-based Haskell project can be recognized without globally installed tools while existing detection still works.

Written by the indexing model from the issue text.

Assessment

Tech stack
emacs-lisp, haskell
Domain
tooling
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.