Sunday, August 23, 2015

Handling Dependencies in Haskell

At each Haskell compilation using a .cabal file, the compiler checks for the dependencies and downloads them. This sometimes leads to duplication of installation/ re-installation, which eventually results in recursive dependencies and build errors of the application.

This can be seen in couple of different ways, they are; 

  • Recursive dependencies
cabal: Error: some packages failed to install: snap-core- failed during the building phase. 
The exception was: ExitFailure 1
  • Hidden packages/modules
    Could not find module ‘Safe’
    It is a member of the hidden package ‘safe-0.3.9’.
    Use -v to see a list of the files searched for.
  • Uncertainty of the package by which a particular module is being imported

    Ambiguous occurrence ‘lookup’
    It could refer to either ‘CMD.lookup’, defined at                 CMD.hs:12:20 or ‘Prelude.lookup’,  
         imported from ‘Prelude’ at CMD.hs:3:8-10 (and              originally defined in ‘GHC.List’)

  • Could not resolve dependencies, Backjump limit reached
cabal: Could not resolve dependencies:
trying: snap- (user goal)
Backjump limit reached (change with --max-backjumps)

To solve them there are numerous ways. Here I'm going to share some methodologies I know to go around the above issues.
  • Solution to recursive dependencies
The reason behind recursive dependencies is; registering the same package in both System directory and User directory of cabal. In order to solve this you should take a look at the packages installed with the 'ghc-pkg list' command as follows;
It lists both System directory packages and User directory packages.

System directory packages
User directory packages

 If you can eye the repeating packages you can unregister them using;
'ghc-pkg unregister <packageName | packageName-version>'
  • Solution to shadowed packages/ modules
In case of a hidden package you can see in the package list in parenthesis as follows;

Here the packages 'monad-tf-' and 'network-uri-' are hidden. You can expose those packages using;
'ghc-pkg expose <packageName | packageName-version>'
In a scenario where there are hidden modules, there is no solid solution in order to use the same module in your code except for switching to another module in a different package, because this is done by the author of the package at the time of publishing it. You can check this using the following command for the specific package, which the module is imported from;
 'ghc-pkg describe <packageName>'
The result of that command for the 'http-client'

 Here, you can see the hidden modules and the exposed-modules from the package. 
  • Solution to uncertainty of package by which a particular module is being imported
The best way to solve this is to import packages with the "qualified" tag when you are in doubt whether there are multiple packages importing the methods with the same name. 
Another way is to import the packages as follows;
 'import Data.Aeson as DA'
That way you can always import the methods from the respective package like this,
..... let s = DA.encode json 
 which will make anyone reading the code to understand it easily as well.

These are some ways, I thought the best ways to solve the dependency problems in Haskell. In any case if you mistakenly unregister a package, you can always install it again by using 
'cabal install <packageName>'

  • Solution to Backjump limit reached, which lead unresolved dependencies
A sample of this error, when installing packages in cabal is as follows;

This error occurs when the dependency tree searched exhaustively. One way to solve this is to arrange the dependencies into an order, which will make it easier for the compiler resolve them. To do this we can use,
'cabal install <packageName> --reorder-goals' 
Another solution, as the compiler suggests is to change the 'max-backjumps' from the default to a preferred value. 

Hope this post helped you !!