How do I use PackageImports properly in ghci? - haskell

I want to do testing in ghci and I'm running into problems with similarly named modules in different packages (this is basically a continuation of my previous question: What should I do if two modules share the same name?)
The packages in question are crypto-api, cryptonite, and DRBG. They all provides modules involving Crypto.Random. I'm using PackageImports in ghci to work fix this issue.
However, it seems that the order of using PackageImports is important.
This works (in ghci):
:set -XPackageImports
import Crypto.Random.DRBG
import "crypto-api" Crypto.Random
This does not work:
:set -XPackageImports
import "crypto-api" Crypto.Random
import Crypto.Random.DRBG -- Error!
Also this does not work:
:set -XPackageImports
import "crypto-api" Crypto.Random
import "DRBG" Crypto.Random.DRBG -- Error!
The error that appears is:
<interactive>:1:1: error:
Ambiguous interface for ‘Crypto.Random’:
it was found in multiple packages:
crypto-api-0.13.2 cryptonite-0.23
Am I doing something wrong or is this a bug?

Perhaps the easiest way is to avoid PackageImports and instead rename the packages when starting ghci, using the -package option:
ghci -package "crypto-api (Crypto.Random as A)" -package "cryptonite (Crypto.Random as B)"
Once in ghci, you could import the renamed modules:
Prelude> import A
Prelude A> import B
Prelude A B>
The syntax -package "crypto-api (Crypto.Random as A)" makes only the Crypto.Random module available to ghci, but not the other modules in the package.
According to the Thinning and renaming modules section of the GHC user guide:
We also support renaming of modules, in case you need to refer to both
modules simultaneously; this is supporting by writing OldModName as
NewModName, e.g. -package "base (Data.Bool as Bool). You can also
write -package "base with (Data.Bool as Bool) to include all of the
original bindings (e.g. the renaming is strictly additive).
So perhaps it's better to write the options like -package "crypto-api with (Crypto.Random as A)" to maintain access to all the modules.
Using PackageImports instead of thinning and renaming modules has the problem that it makes the source code dependent on the precise packages in which imported modules live. If a module changes packages that breaks the program.

Why would you expect that to work at all?
:set -XPackageImports
import "crypto-api" Crypto.Random
import Crypto.Random.DRBG -- Error!
import "DRBG" Crypto.Random.DRBG -- Error!
You have imported the DRBG module twice and from the same package. From your question I thought you'd want to get the Crypto.Random modules from crypto-api and cryptonite but I don't see any attempt to use cryptonite at all here.
Just so we're clear, I can reproduce the error I think you were getting via GHCi:
Prelude> :set -XPackageImports
Prelude> import "crypto-api" Crypto.Random as OriginalRandom
Prelude OriginalRandom> import "cryptonite" Crypto.Random as ConflictRandom
<interactive>:1:1: error:
Ambiguous module name ‘Crypto.Random’:
it was found in multiple packages:
crypto-api-0.13.2 cryptonite-0.24
But it all works when you load it from a file so this is just a bug and can be avoided:
% cat t.hs
{-# LANGUAGE PackageImports #-}
import "crypto-api" Crypto.Random as OriginalRandom
import "cryptonite" Crypto.Random as ConflictRandom
import Crypto.Random.DRBG -- This only appears in one package
% ghci t.hs
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /Users/tommd/.ghci
[1 of 1] Compiling Main ( t.hs, interpreted )
Ok, 1 module loaded.
*Main>
Notice that DRBG is not part of this discussion. It doesn't have any conflicting modules names with anything on hackage as far as I know.

Related

GHCi can't load script that imports `Data.Set` or `Data.Maybe` if external package is imported

I have a script
#!/usr/bin/env stack
{-# LANGUAGE QuasiQuotes #-}
module Script where
import qualified Data.Set as Set (Set, fromList)
import Data.Maybe (fromJust)
{- import Text.RawString.QQ not neccessary for demonstration -}
main :: IO()
main = print "foo"
That I run with stack --resolver lts-12.5 runghc --package raw-strings-qq Script.hs
But I can't load it in GHCi:
$ stack ghci --resolver lts-12.5 --package raw-strings-qq
Note: No local targets specified, so a plain ghci will be started with no package hiding or package options.
If you want to use package hiding and options, then you can try one of the following:
* If you want to start a different project configuration than /home/t/.stack/global-project/stack.yaml, then you can use stack init to create a new
stack.yaml for the packages in the current directory.
* If you want to use the project configuration at /home/t/.stack/global-project/stack.yaml, then you can add to its 'packages' field.
Configuring GHCi with the following packages:
GHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /tmp/haskell-stack-ghci/2a3bbd58/ghci-script
Prelude> :load Script.hs
[1 of 1] Compiling Script ( Script.hs, interpreted )
Stacko.hs:7:1: error:
Could not find module ‘Data.Set’
Perhaps you meant Data.Int (from base-4.11.1.0)
Use -v to see a list of the files searched for.
|
7 | import qualified Data.Set as Set (Set, fromList)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
Own research:
I can load the file if I start ghci with just stack ghci --resolver lts-12.5.
Interestingly, when I import Data.Set 'unqualified' as I do with Data.Maybe:
import Data.Set
import Data.Maybe
the error persists, but when I drop the Data.Set import all together I can load Script.hs
Imports that don't work: Data.Set, Data.Map.Strict
Imports that work: Data.List, Data.Function, Data.Maybe
packages that produce the error: raw-strings-qq, HUnit

Is there a ghci colon-command to hide a package?

At present I'm using ihaskell to learn about some libraries. IHaskell is still hazardous, and I can not do:
-- This doesn't work: ":ext PackageImports", the kernel hangs
-- This doesn't work: "{-# LANGUAGE PackageImports #-}", the kernel hangs
import Codec.Crypto.RSA.Pure
import qualified "crypto-api" Crypto.Random as CR
import Control.Monad.CryptoRandom
Without "PackageImports", I get an error message:
Ambiguous interface for ‘Crypto.Random’: it was found in multiple packages: crypto-api-0.13.2 cryptonite-0.7
My question is, is there a colon command inside ghci that would allow me to hide the cryptonite package?
Yes.
:set -hide-package cryptonite

Specifying package name for module-related commands in ghci

Is there a way to specify the package name for a module for the :browse, :load or :module commands in ghci (version 6.12.1) ?
Some module names are ambiguous:
Prelude> :module Control.Monad.Cont
<no location info>:
Ambiguous module name `Control.Monad.Cont':
it was found in multiple packages: mtl-1.1.0.2 monads-fd-0.1.0.2
Is setting the -hide-package option the only thing I can do to avoid the ambiguity?
As far as I know, yes. But it doesn't have to be a big deal, you can do this inside ghci:
Prelude Data.List> :set -hide-package mtl
package flags have changed, resetting and loading new packages...
Prelude> import Control.Monad.Cont
Prelude Control.Monad.Cont>
There was also a line-item on GHC-7 change log that made me think package imports would work on the command line, but it doesn't seem to yet (see below). The change comment said something like "full import syntax supported in GHCi", which must exclude extensions I suppose.
$ ghci-7.0.0.20100924 -XPackageImports
GHCi, version 7.0.0.20100924: http://www.haskell.org/ghc/ :? for help
...
Prelude Data.List> import "mtl" Control.Monad.Cont
<no location info>:
Ambiguous module name `Control.Monad.Cont':
it was found in multiple packages: mtl-1.1.1.0 monads-fd-0.1.0.2
Is setting the -hide-package option the only thing I can do to avoid the ambiguity?
You can use ghc-pkg, e.g.
$ ghc-pkg hide monads-fd
This is like setting -hide-package on every subsequent ghc invocation. Packages explicitly depending on monads-fd via Cabal will not be affected, but everything else is. Watch out!

How to configure GHCi to automatically import modules

When I use GHCi, I almost always end up importing Control.Applicative, Data.List, etc. . Is there a way to configure GHCi to automatically import those modules.
Also, after importing them, how do I keep the prompt from being insanely long?
Prelude Control.Applicative Data.List Database.HDBC Database.HDBC.Sqlite3 System.Directory>
GHCi looks for its configuration file at
~/.ghc/ghci.conf on Unix-like systems.
%APPDATA%\ghc\ghci.conf on Windows.
The configuration file syntax is simple: it's a list of GHCi commands to execute on startup.
For example, your ghci.conf could contain:
import Control.Applicative
import Data.Char
import Data.List
:set prompt "> "
The last line sets the prompt to "> " so it won't show all the modules you imported on the command line.
Now you can get to work right away:
GHCi, version 6.12.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
> toLower <$> "Hello, world!"
"hello, world!"
>
Also, if you decide you don't want Data.Char in the middle of a GHCi session, you can remove it with:
:m -Data.Char
and if you decide you don't want anything but Prelude during a session:
:m
GHC will also load any .ghci file it finds in the current directory. It's very useful to do per-project configuration of GHCi.
This is an example from a project I work on:
:set -isrc:dist/build/autogen
:set -hide-package mtl
The first is there to make sure that the modules generated by Cabal are easy to import. The second hides mtl since this particular project uses transformers.

How can I import a Haskell module in GHCi?

I am trying to teach myself Haskell from the book Learn You A Haskell for Great Good. I got up to the last section of chapter 7 (Modules), where it tells how to create your own module. I did a copy and paste of the Geometry module given in the book at the beginning of the section. The name of the file is Geometry.hs, as the book suggested, and the file is in the bin directory for ghci, which is where I previously was able to successfully do a load using :l for another .hs file.
When I type the following command in GHCi
import Geometry
I get the following error:
Could not find module 'Geometry' It is not a module in the current
program or in any known package
I must be doing something that is obviously wrong, but I can't figure out what it is.
When you use import ModuleName in GHCi, it works (mostly) in the same way import Data.List works: GHC checks your local package database for the module, loads it, and brings its (exported) contents into scope.
However, Geometry isn't a module of a package installed with ghc-pkg. Therefore, GHC doesn't know that a module Geometry exists at all. Neither does it interactive variant GHCi.
But if you :load a program, things change. GHC will take its used modules into account:
-- Foo.hs
module Foo where
foo :: IO ()
foo = putStrLn "Hello from foo!"
-- Main.hs
module Main where
import Foo (foo)
main :: IO ()
main = foo
$ cd /path/to/your/files
$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> import Foo
<no location info>:
Could not find module ‘Foo’
It is not a module in the current program, or in any known package.
Prelude> :l Main.hs
[1 of 2] Compiling Foo ( Foo.hs, interpreted )
[2 of 2] Compiling Main ( Main.hs, interpreted )
Ok, modules loaded: Main, Foo.
*Main> :l Main.hs
*Main> foo
Hello from foo!
*Main> import Foo
*Main Foo> -- module now loaded
As you can see, importing Foo first failed. However, after we've actually loaded the program that uses Foo, we were able to use import Foo in GHCi.
So if you want to use import in GHCi, make sure that GHC can find your module, either by including it in a wrapper or installing it. If you just want to load the module itself, use :load.
TLDR: the Learn you a Haskell book fails to mention that you have to :load the Geometry.hs file first. Then :m to go back to Prelude and then import Geometry works.
It is now also possible to add the lib flag when installing packages, i.e. to run cabal install --lib packagename and then to import the corresponding package directly in GHCi. In the present case, for example cabal install --lib hgeometry would facilitate importing modules from this geometry package.

Resources