How to configure .ghci file to import all loaded modules - haskell

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.

Related

Cabal install package in local directory not reflecting while importing in file

I am currently self studying Haskell. I am just a beginner so I haven't yet had a need to use cabal or stack. But right now I need to test some of my code using QuickCheck.
From this link that I found https://github.com/haskell/cabal/blob/master/doc/cabal-commands.rst , I ran the command cabal install --lib QuickCheck --package-env . and got the following output :
axiom#pop-os:~/Desktop/Haskell-Learning/Course/Homework 10$ cabal install --lib QuickCheck --package-env .
Resolving dependencies...
Up to date
In the same directory, I have a .hs file and in that when I tried to import Test.QuickCheck the linter gives an error as the package doesnt seem to be available for importing.
Then I ran cabal repl --build-depends QuickCheck and then in ghci I was able to import it. But still it was not importing in the code file.
Then when I just opened ghci by firing the command ghci , the following shows up, which suggests that there is a package environment here in this directory :
GHCi, version 8.10.7: [https://www.haskell.org/ghc/](https://www.haskell.org/ghc/) :? for help
Loaded package environment from /home/axiom/Desktop/Haskell-Learning/Course/Homework 10/.ghc.environment.x86\_64-linux-8.10.7
Prelude> import Test.QuickCheck
Prelude Test.QuickCheck> :q
Even after above, that is, being able to import QuickCheck in GHCi, the import is still not working in the file.
After this, I tried the following :
axiom#pop-os:~/Desktop/Haskell-Learning/Course/Homework 10$ cabal install QuickCheck
Resolving dependencies...
Up to date
Warning:
############################################################
# WARNING: Installation might not be completed as desired! #
############################################################
The command "cabal install [TARGETS]" doesn't expose libraries.
* You might have wanted to add them as dependencies to your package. In this
case add "QuickCheck" to the build-depends field(s) of your package's .cabal
file.
* You might have wanted to add them to a GHC environment. In this case use
"cabal install --lib QuickCheck". The "--lib" flag is provisional: see
https://github.com/haskell/cabal/issues/6481 for more information.
axiom#pop-os:~/Desktop/Haskell-Learning/Course/Homework 10$ cabal install --lib QuickCheck
Resolving dependencies...
Up to date
The import still doesn't work.
Any help is appreciated !
The method I ended up finding is the following :
Run the following in the directory where you have your haskell file where you want to import an external package :
cabal init
This will general a small number of files and such in that directory.
Add the name of the package that you want to use in the .cabal file that was generated . For example, I wanted to use QuickCheck so my .cabal file looks like this :
cabal-version: 2.4
name: Homework10
version: 0.1.0.0
author: Name Surname
maintainer: name#email.com
extra-source-files: CHANGELOG.md
executable Homework10
main-is: Main.hs
build-depends: base ^>=4.14.3.0, QuickCheck
hs-source-dirs: app
default-language: Haskell2010
Then in the same directory, run the following :
cabal build
Then when you try to import the package in your .hs, you should be able to do so.

GHCI needlessly recompiles sub-package

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

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.

GHCi cannot find modules of my program

I'm working on a project and I'm using Cabal for management. I've specified directory of source files, modules, all the stuff. All my files have the same names as their corresponding modules, case is preserved.
I can do:
$ cabal configure
$ cabal build
without problems.
However, imagine I have a module Module in file Module.hs, and file File.hs in the same directory. Now, when I'm trying to load File.hs from Emacs for testing, I get the following:
____Could not find module ‘Module’
It is a member of the hidden package ‘ghc-7.8.3’.
Use -v to see a list of the files searched for.
Failed, modules loaded: none.
Full contents of File.hs:
module File where
import Module
How to make it find files of my project?
You can launch the REPL via Cabal like so:
# cabal repl
This is the same as running ghci, but will take into account any additional dependencies installed by cabal install your local or sandbox package repository.
You need to tell GHCi where to find your source files. For example, if your project directory is ./foo and you have your source files in ./foo/src you need to say (from your project directory):
:set -isrc
at the command prompt in GHCi. You will then have access to private members in your sourc file loaded with C-c C-l.
You also need to make sure that you haven't cabal installed your package, otherwise the package will be loaded, not the project source files.
I had the same problem, fixed it, and decided to write about my troubleshooting. This might help new people learning Haskell. Read on.
I was playing around with this example code.
http://zvon.org/other/haskell/Outputdirectory/getCurrentDirectory_f.html
Code:
import Directory
main = aaa "/tmp/FOO"
aaa ddd = do createDirectory ddd
setCurrentDirectory ddd
d <- getCurrentDirectory
print d
writeFile "aaa" "HELLO"
l <- getDirectoryContents d
print l
I noticed that they are using this package.
https://hackage.haskell.org/package/directory-1.3.6.2/docs/System-Directory.html
So I installed it with this commands:
cabal update
cabal install directory
Compiling the example code with ghc failed with this error message.
Could not find module `Directory'
Use -v to see a list of the files searched for.
|
4 | import Directory
| ^^^^^^^^^^^^^^^^
I was stuck for a while until I changed the import line to this:
import System.Directory
After this change ghc could compile successfully.
Conclussion: are you sure you are importing properly?

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