What should I do if two modules share the same name? - haskell

I have two packages that provide modules with the same name. When I try to load the module I get an error
Ambiguous interface for ....: It was found in multiple packages.
What should I do to resolve this?
To be specific, both the cryptonite package and crypto-api package provide modules with the name Crypto.Random. How can I specify which package I want to load the module from?

If you happen to be using ghc >= 8.2 and cabal-install >= 2.0, another option is renaming the conflicting modules in the mixins section of the cabal file:
build-depends: base >=4.10 && <4.11,
cryptonite >= 0.24,
crypto-api >= 0.13.2
mixins:
cryptonite (Crypto.Random as CryptoniteCrypto.Random),
crypto-api (Crypto.Random as CryptoAPICrypto.Random)
You can then import the renamed modules:
module Main where
import CryptoniteCrypto.Random
import CryptoAPICrypto.Random
One thing to take into account when renaming this way is that modules that haven't been explicitly renamed become inaccessible.
In fact, ability to rename modules seems to exist since GHC 7.10, through the -package flag and the reexported-modules cabal section. reexported-modules works at declaration-time (when publishing a package) while mixins works at use-time (when depending on a package).

You can use the PackageImports language pragma and explicitly pick the package you mean in your import statement like so:
import "cryptonite" Crypto.Random
Alternatively if you have both installed but are only using one of them, you could explicitly list the dependencies you use in a cabal file and build via cabal.

Related

How do you import a Haskell module that was installed using Cabal?

I installed the timezone-series Haskell module using cabal install timezone-series-0.1.5.1.
I then defined a module named Main.hs that starts with:
import Data.Time.LocalTime.TimeZone.Series -- from timezone-series-0.1.5.1
when I run ghc Main.hs, GHC throws the following error:
/home/ubuntu/Main.hs:2:1: error:
Failed to load interface for ‘Data.Time.LocalTime.TimeZone.Olson’
I tried explicitly including the cabal directory in GHC's search path using:
ghc -i/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/ Main.hs
This resulted in the correct path being searched, but GHC is only looking for files ending in the suffix ".hs":
Locations searched:
...
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.hs
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.lhs
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.hsig
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.lhsig
Cabal installed interface files instead however:
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Olson.hi
From line 318 of GHC's source code it looks like GHC ignores "*.hi" files unless it is called in single-shot mode (with the -c flag). Is this correct? (See: https://github.com/ghc/ghc/blob/67a5a91ef5e61f3b3c84481d8a396ed48cd5d96e/compiler/GHC/Unit/Finder.hs)
How can I get GHC to import this module?
An help will be greatly appreciated!
My suggested ways of installing packages in order of my preference:
Make a cabal package and add timezone-series you want to install to the build-depends field as described in the cabal manual.
Use the experimental cabal-env tool to basically automate the process of point 3 below, but then with the global environment. This makes a new build-plan every time you install a new package, so it is like removing the package environment and building it again with all the old packages and the new package added to it. You can add specific constraints like this: cabal-env "timezone-series == 0.1.5.1".
Install a package into local package environment with cabal --package-env . --lib timezone-series. You can add as many packages as you want after the --lib option to install more than one package. If you later want to use a different set of packages simply remove the .ghc.environment.* file that is generated and rerun the installation with a new set of packages. GHC will automatically use these package environment files that are in the current or parent directories. You can specify specific constraints with the --constraint option like this: --constraint "timezone-series == 0.1.5.1".
Use cabal install --lib timezone-series to install it directly into the global environment (~/.ghc/x86_64-linux-8.0.2/environments/default), this will fail if a conflicting package was installed earlier. When you run into errors you can remove that package environment and try again.
Finally, I want to note that GHC 8.0.2 is quite old, so I would advise you to upgrade if you don't have a specific reason for using that version.

How can I pin a version of a Haskell dependency to a version of an underlying native dependency with Cabal?

In my particuar case, I have a dependency in my Cabal file on the Haskell package bindings-libzip. In particular, I could accept several different versions of libzip, e.g. bindings-libzip-0.11 or bindings-libzip-0.10. These in turn have a dependency on the respective native C libzip libraries versions 0.11 and 0.10.
Therefore I have bindings-libzip >= 0.10 < 0.12 in my .cabal file.
The Haskell package bindings-libzip-x specifies with PkgConfig-Depends that libzip version x must be present on a client machine. Let's say a downstream user has version 0.10 of libzip installed. However, when pulling down my package, this user pulls down the latest dependencies possible and transitively pulls down version 0.11 of bindings-libzip. This causes the build process to error out with a message about an incorrect version of libzip installed.
Is there any way I can specify in my .cabal file to use bindings-libzip-0.11 if and only if pkg-config detects version 0.11 of libzip and to use bindings-libzip-0.10 if and only if pkg-config detects version 0.10 of libzip?
I'm submitting another answer because this uses another idea...
Using a custom Setup.hs with defaultMainWithHooksArgs allows you to inspect and modify the args to the cabal configure command.
This is a Setup.hs which does no modification:
import Distribution.Simple
import Distribution.Simple.Configure
import System.Environment
main = do
args <- getArgs
defaultMainWithHooksArgs simpleUserHooks args
If your .cabal file has a flag defined, e.g.:
Flag Foo
Default: False
then in the args you will see "--flags=-foo". So the idea is:
Define two flags in the .cabal file - use10 and use11 to select which version of bindings-libzip to use.
In your custom Setup.hs determine which version to use.
Find the "--flags=..." arg and modify it appropriately before passing it along to defaultMainWithHooksArgs.
I think the way to do this is with a custom Setup.hs file (specify build-type: Custom in the .cabal file.)
You can override specific stages of the build process by using a main like this:
main = defaultMainWithHooks $ simpleUserHooks { preConf = myPreConf }
myPreConf :: Args -> ConfigFlags -> IO HookedBuildInfo
myPreConf args configFlags = ...
It is also likely that overriding the confHook is what you want.
Some links:
all cabal hooks: data UserHooks
standard sets of hooks: (link)
Examples of custom Setup.hs files overriding confHook:
abcBridge
arb-fft
cabalmdvrpm
darkplaces-text
GLFW
happybara-webkit-server
haskeline
HDBC-postgresql
helics
hlbfgsb
hlibsass
hpqtypes
hruby
hsqml
hubris
illuminate
intel-aes
keera-posture
KiCS-debugger
libpq
llvm-general
morfeusz
postgresql-libpq
tamarin-prover
tamarin-prover-term
tamarin-prover-theory
tamarin-prover-utils
voyeur
wxc
wxcore
In particular, the hruby Setup.hs looks like it is doing something like what you want to do.

Haskell Hidden Packages: Data.HashSet

I'd like to use Data.HashSet in Haskell. So I put import Data.HashSet at the beginning of my program. GHCi complains: Could not find module Data.HashSet.
My questions are:
How can I get Data.HashSet to work?
I've read somewhere that this is part of a hidden module or package. Why is the module hidden? Does "hidden" mean that I should not use it?
If I should not use it, is there a better alternative for a haskell data structure with a near-constant lookup time?
Data.HashSet is a module in the unordered-containers package, and also in the hashmap package. If you have either package installed,
import Data.HashSet
should work out of the box, since it is an exposed module of both packages. To install it (using unordered-containers, since that is the commonly used one),
cabal update
cabal install unordered-containers --dry-run
-- check that it wouldn't reinstall anything, if all's fine
cabal install unordered-containers

Ambiguous module name `Prelude'

I get this when i want to recompile xmonad to change the configuration:
Implicit import declaration:
Ambiguous module name `Prelude':
it was found in multiple packages: base haskell98-2.0.0.0
Xmonad was installed via pacman. When i got this error i removed xmonad from pacman and then tried to cabal install xmonad. I got the above error again but i was able to solve it by removing haskell98 from the cabal file.
Now i want to reconfigure xmonad with MOD-Q the error reappears and i have no clue how to fix this. Any help appreciated.
I use GHC version 7.0.3 (from Haskell platform)
Try this:
ghc-pkg hide haskell98
In my case hiding haskell98 unfortunately was insufficient, I had to remove the obsolete haskell98 from the build-depends list in my .cabal build file (keeping the base >= 4 of course).
It read before:
build-depends:
base >= 4,
haskell98
... and then ...
build-depends:
base >= 4
With that the error message "Ambiguous module name `Prelude'" above disappeared.
See this GHC bug ticket on the same subject:
GHC starting with version 7.2.1 will not support using the haskell98
package and the base package at the same time. The haskell-src package
has both of these in its build-depends, so it will need to be
modified. Most of the time, what you want to do is remove haskell98
from build-depends, and fix up any imports of Haskell 98 modules to
point to their base equivalents.
The bug ticket was closed (without a fix), and the original filer responded:
Yes, removing haskell98 from .cabal file seems to have fixed it - it
did compile without errors. It looks like it didn't have any imports
to haskell98, because according to comment in .cabal file.

Control.Monad.State found in multiple packages haskell

While evaluating the line "import Control.Monad.State" in a Haskell module, GHC gives me the following error:
Could not find module `Control.Monad.State':
it was found in multiple packages: monads-fd-0.0.0.1 mtl-1.1.0.2
Failed, modules loaded: none.
How do I resolve this conflict?
You have several options. Either:
ghc-pkg hide monads-fd. This will cause GHC and GHCi to ignore the presence of the monads-fd by default until you later ghc-pkg expose monads-fd, but software installed by Cabal will still be able to build against it.
Use the {-# LANGUAGE PackageImports #-} pragma, and change your import statement to import "mtl" Control.Monad.State.
Use Cabal to build your project, and specify mtl in the Build-depends line.
The first is best for casual hacking, and the last is best for production builds.
These all assume you want the mtl module and not the monads-fd module; otherwise swap them.
Both packages implement Control.Monad.State and GHC does not know which implementation it should prefer, so you need to hide one of the packages from GHC. Seems like the -ignore-package <name> GHC flag might help you here.

Resources