GHCI needlessly recompiles sub-package - haskell

I have a project (let's call it parent) that has a sub-package (child). Using HLS from emacs, whenever I change a file in the parent that imports part of the child package, and try to load it, GHCI will recompile the whole sub-package again every time. The sub-package has lots of TH code in it and takes a long time to compile, which really messes with your workflow when you just want to check if something works. Any ideas?
I'm using
stack 2.7.5 with resolver lts-18.28
cabal 3.6.2.0
GHC 8.10.7
HLS 1.7.0.0
My stack.yaml in the parent package:
resolver: lts-18.28
packages:
- .
- sub-package
extra-deps:
- ... (omitted)
allow-newer: true
EDIT: Minimal example: git repo
file details:
parent stack.yaml
resolver: lts-18.28
packages:
- .
- child
child stack.yaml
resolver: lts-18.28
packages:
- .
parent main src/Parent.hs:
module Parent where
import Child
someFunc :: IO ()
someFunc = childFunc
child main file child/src/Child.hs:
module Child where
childFunc :: IO ()
childFunc = putStrLn "someFunc"

I'm not sure how this handshakes with HLS, but when you run stack ghci, by default it loads modules from all packages in the project into GHCi, all in an interpreted form, which means recompiling Child.hs every time GHCi starts.
However, if you specify the parent package (here, named ghci-test) on the command line: stack ghci ghci-test, it will only load modules from that package and use the compiled version of child (which it will build, if necessary, prior to switching over to the GHCi prompt).
Some caveats:
You will not be able to load anything from the child package into this GHCi session (e.g., :l Child will fail).
Changes to the child package won't take effect unless you quit and restart GHCi.

Turns out adding -fobject-code to the .ghci file causes ghci to create object files, which in turn saves it the trouble of recompiling everything every time.
GHC reference

Related

How to configure .ghci file to import all loaded modules

let say I have a project which is just a bunch of Haskell modules with exercises. I'd like to provide a .ghci which automatically imports all modules into ghci scope. The problem is, I can not run import nor :m +Module.Name within .ghci file.
Clearly cabal repl is reading the .ghci file because options like the prompt are readed. Also it loads modules correctly, but it doesn't bring them into scope (only one gets imported). If I try to add import OtherModule to .ghci file, then I get the error
module is member of hidden package fail-ghci
Perhaps you need to add ‘fail-ghci’ to the build-depends in your .cabal file.
But I can't add fail-ghci to the cabal file, because the library can't depend on itself!!
To reproduce. Create a simple cabal project. For example:
src
|- Module1.hs # include dummy function func1 :: Int
|- Module2.hs # include dummy function func2 :: Int
fail-ghci.cabal
.ghci
The content of fail-ghci.cabal is
name: fail-ghci
version: 0.1.0.0
license: BSD3
license-file: LICENSE
build-type: Simple
library
exposed-modules:
Module1
, Module2
hs-source-dirs:
src
build-depends:
base >=4.7 && <5
default-language: Haskell2010
If you set .ghci as
:set prompt "> " -- Set this option to ensure .ghci is readed by cabal repl
It will work fine and will bring Module1.hs into scope, so func1 is available. But Module2.hs wont be in the scope, If I want to use it I'd need to execute import Module2 or equivalent.
Now, I'd like this to happen automatically when running cabal repl because my project has many modules. The obvious (to me) choice is to modify .ghci as
:set prompt "> " -- Set this option to ensure .ghci is readed by cabal repl
import Module2 -- Equivalent :m +Module2
But the ghci is unable to import the module, despite of that command woring correctly within ghci. What's the correct configuration to do this?
One workaround I like is to add a new module to your project, perhaps called DefaultRepl or similar, that imports everything you want. If you put this at the top of your exposed-modules list, then it will be loaded up when you run cabal repl and bring everything that it imports into scope.
I suppose the problem is simply that cabal executes the commands in your .ghci file before it loads the current project.
Indeed when I cabal repl in a similar project, the startup output is:
Build profile: -w ghc-9.0.2 -O1
In order, the following will be built (use -v for more details):
- fail-ghci-0.1.0.0 (first run)
Preprocessing library for fail-ghci-0.1.0.0..
GHCi, version 9.0.2: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /tmp/scratch/.ghci
[1 of 2] Compiling Module1 ( src/Module1.hs, interpreted )
[2 of 2] Compiling Module2 ( src/Module2.hs, interpreted )
Ok, two modules loaded.
ghci>
My guess would be that this is probably because cabal repl starts a ghci session (which loads the .ghci file during startup) and then uses its API to load the project environment. (I was able to confirm that a .ghci file can import modules from packages that are fully installed, rather than from a current project being cabal repld)
I don't know of any way to automatically execute commands after the project has been loaded. But you can have your .ghci file define a shortcut command that will import all the modules you want. That way you have at least have a single quick command to execute once you're in the ghci shell, instead of manually importing everything. Something like this:
:def loadall (const . pure) ":m + *Module1 *Module2"
-- or
:def loadall (const . pure) "import Module1 Module2"
-- or etc
Then you can just enter :loadall at the ghci prompt to get your modules imported.

Cabal update now can't load any modules from "hidden packages"

I've been working on a project and recently I did a cabal update.
I usually roll into ghci like:
$ ghci -package-db ~/.cabal/store/ghc-8.10.7/package.db
After the update loading module in my project results in even the basic Haskell modules like System.Random or MonadIO fails with the following errors when trying to load my own module called ProcessIO:
ProcessIO.hs:50:1: error:
Could not load module ‘Data.IORef.MonadIO’
It is a member of the hidden package ‘monadIO-0.11.1.0’.
You can run ‘:set -package monadIO’ to expose it.
(Note: this unloads all the modules in the current scope.)
Locations searched:
Data/IORef/MonadIO.hs
Data/IORef/MonadIO.lhs
Data/IORef/MonadIO.hsig
Data/IORef/MonadIO.lhsig
I checked that maybe the .cabal file build-depends versions might have been altered, but the cabal package.db directory contains all the right versions of the dependencies in the .cabal file. For example the error above complains abot monadIO-0.11.1.0 being hidden however: in package.db/ we see the right version exists:
monadIO-0.11.1.0-0aec75273f3fef94783e211a1933f8ac923485a963be3b6a61995d4a88dd1135.conf
I should say I haven't looked at the package.db files before because everything simple worked so there may be something telling about the .conf file name that signals something is wrong.
Either way, can't build anything and I need some help!
EDIT: posting my default environments file ~/.ghc/x86_64-linux-8.10.7/environments/default in case it matters:
clear-package-db
global-package-db
package-db /home/surya/.cabal/store/ghc-8.10.7/package.db
package-id ghc-8.10.7
package-id bytestring-0.10.12.0
...
(Let me know if I need to share more of it... or less)

stack ghci (intero): import between two standalone files

I have just two files A.hs and B.hs in a directory dir (so no cabal or stack.yaml files). A imports B (with just import B). Running stack ghci and loading A then fails, complaining about the import. Is there a way to get this working? Running the system-wide ghci, everything works fine of course.
The reason I'm asking is that I've switched to intero for Emacs, which uses stack, and I'd like to be able to use intero for random small bits of code that I have lying around.
Versions: ghc 8.0.1, stack 1.1.2.

ghc-mod under stack complaining about hidden main package

I have following problem with ghc-mod which prevents me from using ide for some files in a yesod app project.
I install template app as follows:
/tmp$ stack new demo yesod-sqlite && cd demo
/tmp/demo$ stack setup && stack build && stack install ghc-mod
Which yields following stack.yaml (commented lines removed):
resolver: lts-5.6
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []
And this is a demo.cabal: http://pastebin.com/i4n1TR6W.
Then, running stack exec -- ghc-mod check app/main.hs does not produce errors, but stack exec -- ghc-mod check app/devel.hs has this to say:
app/devel.hs:2:1:Failed to load interface for ‘Application’It is a member of the hidden package ‘demo-0.0.0’.Perhaps you need to add ‘demo’ to the build-depends in your .cabal file.
So the ghc-mod somehow thinks this package is itself hidden? But any other place where project's files are imported by another checks fine, and the application builds and works successfully. The only specifics about this file is using PackageImports language extension:
{-# LANGUAGE PackageImports #-}
import "demo" Application (develMain)
I tried googling the error message but it seems to only come up with regard to external packages and not the one being debugged.
These two files devel.hs and DevelMain.hs are quite special: they are marked as a module of demo in .cabal but they are importing demo as a compiled package, i.e. recursive dependency.
They are not exposed from library demo nor imported anywhere else so won't get compiled when you run stack build, but when you run ghc-mod check on them, they are interpreted in the context of the current project, therefore the recursive dependency will be an issue.
The only purpose of these two otherwise meaningless files is to debug your yesod website in ghci, as the comment in DevelMain.hs stated:
-- | Running your app inside GHCi.
--
-- To start up GHCi for usage with Yesod, first make sure you are in dev mode:
--
-- > cabal configure -fdev
--
-- Note that #yesod devel# automatically sets the dev flag.
-- Now launch the repl:
--
-- > cabal repl --ghc-options="-O0 -fobject-code"
--
-- To start your app, run:
--
-- > :l DevelMain
-- > DevelMain.update
--
-- You can also call #DevelMain.shutdown# to stop the app
--
-- You will need to add the foreign-store package to your .cabal file.
-- It is very light-weight.
--
-- If you don't use cabal repl, you will need
-- to run the following in GHCi or to add it to
-- your .ghci file.
--
-- :set -DDEVELOPMENT
--
-- There is more information about this approach,
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
cabal repl and stack ghci will compile the project beforehand so these two files won't cause any error there.

Different behavior of cabal repl for library vs. executable

Using cabal repl seems to do nothing at all when used on library projects, but works fine for executable projects. Is this expected behavior that I just don't understand?
If I have a file containing simply
go = putStrLn "test"
and use cabal init with all the defaults (but choose "library" as the type), then running cabal repl just produces the some text about configuring and preprocessing the library and never enters a REPL environment. The exact same steps, but with "executable" selected as the type, puts me right into GHCi as expected.
The code works fine when loaded directly into GHCi.
For cabal repl to load your modules, you have to first name them in code and then specify them in your project's .cabal file as exposed:
-- MyModule.hs
module MyModule where
go = putStrLn "test"
-- MyProject.cabal
name: MyProject
-- other info ...
library
exposed-modules: MyModule
-- other options ...
Then when you run cabal repl, it'll have access to everything in your sandbox (if present) and the exposed modules. It might also work if you specify them as other-modules instead of exposed-modules, but I haven't tried that one out.

Resources