What is the cabal equivalent of `stack ghci app:exe:executable`? - haskell

I've previously used stack ghci app:exe:executable to get a list of any errors in my Haskell project managed via cabal.
However now that I'm not using stack, how would I achieve the above (essentially load all the modules from the executable defined in the cabal project file)?

https://cabal.readthedocs.io/en/latest/nix-local-build.html#cabal-v2-repl
cabal v2-repl executableNameGoesHere

Related

Compiling a haskell script with external dependencies without cabal

I'm relatively new to Haskell and I realize I might be swimming against the stream here, but nonetheless, I'll ask:
Say I have a short Haskell script:
import Data.List.Split (splitOn)
main :: IO ()
main = do
let orders = splitOn "x" "axbxc"
putStrLn $ head orders
If I used only standard functions I could compile this with ghc <script.hs>. Because I depend on the split package to provide the splitOn function, the compilation fails.
Now, I have no difficulties setting up a cabal project with a project.cabal and a Setup.hs file in order to get this to actually compile. However, this feels like a lot of extra boilerplate for a standalone script.
So, is there a way to compile a single .hs file against some external package? Something similar to what in Python would be done by pip install something, "installing the package into the interpreter", i.e. is there a way to install extra packages "into ghc", so that I for instance only need to provide some extra linking flag to ghc?
The Cabal equivalent of the Stack script in bradrn's answer would be:
#!/usr/bin/env cabal
{- cabal:
build-depends: base
, split
-}
import Data.List.Split (splitOn)
main :: IO ()
main = do
let orders = splitOn "x" "axbxc"
putStrLn $ head orders
The script can be run with cabal run, or directly by giving it execute permission. If need be, version bounds can be added as usual to the build-depends on the top of the script.
(Note this isn't literally a solution without Cabal, as doing this with GHC alone, even if it is possible, wouldn't be worth the trouble. In any case, it certainly avoid the boilerplate of needing multiple files.)
If you use Stack, the simplest way to do this is to write a ‘Stack script’, which is a Haskell file with a description of the required packages in the first line (really an invocation of stack specifying the appropriate command line arguments). An example (slightly modified from the docs):
$ cat turtle-example.hs
-- stack --resolver lts-6.25 script --package turtle
{-# LANGUAGE OverloadedStrings #-}
import Turtle
main = echo "Hello World!"
$ stack ./turtle-example.hs
Completed 5 action(s).
Hello World!
$ stack ./turtle-example.hs
Hello World!
This script uses the turtle package; when run, Stack downloads and builds this dependency, after which it is available in the script. (Note that the second time it is run, turtle has already been built so does not need to be rebuilt again.)
As it happens, the --package command in Stack is not limited to scripts. It can be used with other Stack commands as well! For instance, to compile your program, you should be able to run stack ghc --resolver lts-16.27 --package split -- -ghc-options your-program-name.hs. And stack ghci --package split will give you a GHCi prompt where you can import Data.List.Split.
(Note: This answer focuses on Stack rather than Cabal, simply because I don’t know Cabal very well. However, I believe all this can be done using Cabal as well. For instance, I do know that Cabal has something very similar to the Stack scripts I mentioned above, though I can’t remember the syntax just at the moment.)
EDIT: See #duplode’s answer for how to do this with Cabal.
You can install into the default environment for the current user by doing cabal install --lib split. The package should then be available to ghc and ghci without needing any special options.
More information is at the bottom of this section in the Cabal manual. The v2 commands that it uses are the default now so if you have a fairly new cabal you can just use install rather than v2-install.
I think this is the quintessential entry point to the package management battle in Haskell. It's not there's not enough advice, but there's so much, each with its own caveats and assumptions. Climbing that mountain for the sake of splitOn feels to the newbie like they're Doing It Wrong.
After spending far too much time trying each permutation, I've collated the fine answers here, and many, many others from elsewhere, put them to the test, and summarised the results. The full write up is here.
The pertinent summary of solutions is:
Install globally
You can still do global installs with cabal install --lib the-package.
Use Stack as a run command
You can use stack directly, eg: stack exec --package containers --package optparse-generic [...and so on] -- runghc hello.hs
Create a Stack project
The real deal. Run stack new my-project hraftery/minimal, put your code in Main.hs and your dependencies in my-project.cabal (and maybe stack.yaml - check the article), and then run stack build to automagically pull and build all dependencies.
Use a Stack script
Make your Haskell file itself an executable Stack script. Add -- stack --resolver lts-6.25 script --package the-package to the top of your .hs file and set its executable bit.
For my Edit/Test/Run workflow (eg. using VS Code, GHCi and GHC respectively), I found it pretty clear what works in practice. In summary:
Amongst a chorus of discouragement, Global Installs suit what I know of your use case just fine.
Where Global Installs don't make sense (eg. for managing dependency versions or being portable) a Stack project starting from my minimal template is a smooth transition to a more sophisticated and popular method.

With HaskellStack install packages to use with GHC without stack

I install GHC on Windows10 using the recommended Haskell Stack. I want to us GHC without all the Stack overhead for Advent of Code. This was working fine until I tried to get the extra package.
I can install it with Stack, but I don't seem to have a way to get it in the global package database. Haskell Stack apparently does not install the cabal executable and seems to have it locked out of their package database.
How do I install the extra package for use with vanilla GHC?
John Miller#DESKTOP-NENAGQH MSYS /d/dev/AdventOfCode2020
$ stack ghc -- AoC/Utils.hs
[1 of 1] Compiling AoC.Utils ( AoC\Utils.hs, AoC\Utils.o )
John Miller#DESKTOP-NENAGQH MSYS /d/dev/AdventOfCode2020
$ ghc AoC/Utils.hs
[1 of 1] Compiling AoC.Utils ( AoC\Utils.hs, AoC\Utils.o ) [Data.List.Extra changed]
AoC\Utils.hs:3:1: error:
Could not find module `Data.List.Extra'
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
3 | import Data.List.Extra
| ^^^^^^^^^^^^^^^^^^^^^^
I don't know whether stack supports installing to the global package DB. cabal does though:
$ cabal install --lib extra
Resolving dependencies...
Up to date
$ ghci
GHCi, version 8.10.2: https://www.haskell.org/ghc/ :? for help
Loaded package environment from /home/simon/.ghc/x86_64-linux-8.10.2/environments/default
Prelude> import Data.List.Extra
Prelude Data.List.Extra>
Ok, so stack can install the cabal executable if you beat at it long enough. The package is called cabal-install and it is not in any resolver, but is on Hackage.
stack install cabal-install
Because it is not in the resolver there is a pretty good chance that the version of Cabal, the library for manipulating cabal packages in Haskell, is not compatible. First, ask stack where it keeps its global config
stack path --config-location
Edit that file to allow for the needed dependencies under extra-deps: Stack will helpfully tell you what they are. It may also be helpful to change the resolver to a newer version in that file while your at it.
Now try
stack install cabal-install
again and if these instructions have not fallen out of date since December 2020 you will get the cabal executable somewhere potentially useful.
Before using cabal you will have to run a cabal update to get the package list.
At this point cabal should manipulate your global package database and stack can install GCH and all its libraries over and over and over again if you want to use it for a project instead. They should just keep out of each other's way.

Cabal cannot find locally sourced (yet correctly installed) packages

I recently upgraded to Cabal 3.2 (and GHC 8.10) and I am running into some major issues that make some of my project non-buildable anymore...
Thorough description of the problem
Here is a minimal (not) working configuration that fails every time:
I start off with a clean Cabal configuration (by deleting ~/.cabal); the reason for that will appear later in the post. I run cabal update to recreate the .cabal directory and to ensure Cabal is working.
I create a project (let's call it test1) using cabal init. This is a library project with one exposed module (conveniently named Test1) that exports some dummy function foo. I run cabal build, then cabal install --lib; everything is running smooth, so far so good.
Just to be sure, I leave the project directory and fire up GHCi. I type in :m Test1 to load the module I created earlier, and it works! I can type in foo ... and see my function executed. Also, I list the content of ~/.cabal/store/ghc-8.10.xxx and see that the test1-xxx directory is there.
I then create a new project, test2, still using cabal init. This time, I configure it to be an executable, and I add test1 as a dependency (using the build-depends field). But this time when I run cabal build, I run into some issue:
~/projects/haskell/test2> cabal build
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: test2-0.1.0.0 (user goal)
[__1] unknown package: test1 (dependency of test2)
[__1] fail (backjumping, conflict set: test1, test2)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test2, test1
It seems to me like package test1 cannot be found, however I can access it from GHCi (and GHC for that matters) and it is present in ~/.cabal/store...
But unfortunately there is more.
I create a third project, test3. This is a library, and it depends on nothing else than base (so in particular it does not depend on test1). The lib exposes one module, Test3, with one function exported, bar. I run cabal build, no problem here. But when I want to install test3 with cabal install --lib I run into some errors:
~/projects/haskell/test3> cabal install --lib
Wrote tarball sdist to
/home/<user>/projects/haskell/test3/dist-newstyle/sdist/test3-0.1.0.0.tar.gz
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1
It seems that it cannot find test1, although it has been installed correctly; may be this is a remnant of the failed build of test2 though...
Just to be sure, I fire up GHCi and type in :m Test3, but GHCi tells me that it cannot find module Test3 (and even suggests this is a typo and I was meaning Test1), showing that test3 indeed did not get installed, although it got successfully built...
Okay there is one more quirk to this whole situation: I create once again a new project with cabal init, called test4, which is an executable that (again) depends on nothing else than base. I keep the default Main.hs (that just prints "Hello, Haskell!"). I run cabal build: no problem. Then I run cabal install and... No problem either? I run test4 in a random location and it fires up the executable, printing "Hello, Haskell!" in the terminal...
And there is one last thing: I go to some random location and I run cabal install xxx --lib where xxx is a library package available on Hackage (for example xml) and:
~> cabal install xml --lib
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1
This is the reason why I need to nuke .cabal regularly... Right now I seem to be in some kind of stale state where I cannot install any library anymore.
Technical configuration and notes
I am running Cabal 3.2.0.0 and GHC 8.10.0.20200123. I installed them from the hvr/ghc PPA, and I made sure there are no other versions of those tools anywhere on my computer.
Just as a note, I am running Ubuntu 18.04.4 LTS (with XFCE so XUbuntu to be exact). Everything else (seem to be like it) is up to date.
Last thing, regarding the *.cabal files I use for building, they are pretty much the ones generated by cabal init, except I switch executable xxx for library in the case of libraries, and I simply add a exposed-modules field for exposing modules for the libraries (so Test1 for test1 and Test3 for test3 respectively). I also use build-depends in test2 to make the project depend on test1. Apart from that, they are pretty much left untouched.
Notes and thoughts
I must confess that I am new to Cabal 3; until last week I was using Cabal 1 (because I never bothered to update it; yes I know this is bad). With Cabal 1 I did not have any problem whatsoever, and I was perfectly able to install a package from local sources and depend on it in other projects...
I feel like I am doing something wrong; maybe am I not using the correct Cabal commands? I saw somewhere something about cabal new-build and cabal new-install but it does not seem to do anything more than cabal build and cabal install, at least in my case. I also wanted to investigate sandboxes but it seems that has disappeared since version 2 of Cabal.
There is also a slight possibility this is a Cabal bug, but I don't find any relevant issue on the bug tracker that may be related to my problem...
What do you think about this? What am I doing wrong? Do you see any alternative or possible fix?
Thanks a lot!
GHC environment files
A GHC installation comes with a certain number of packages out-of-the box. base is one of them but there are others, for example text. If you install GHC alone (no cabal or stack) and open ghci, it should let you import Data.Text without problems.
What if you want GHC or ghci to be aware of other compiled packages present in your filesystem? You can point GHC to additional package databases using command-line flags, but there's also the concept of package environment files.
Environments are plain text files that contain a list of package-related GHC flags. There might be a global environment at ~/.ghc/$ARCH-$OS-$GHCVER/environments/default, and there might also exist local environments which only affect GHC and ghci commands invoked inside the same folder. The exact rules for search are described in the GHC User Guide.
What does cabal install --lib actually do?
By default, it modifies the global environment file, so that GHC and ghci can now find that library. That's why point 3) worked. The actual compiled binaries of the library still reside in the cabal store though.
We can also create local environment files. For example cabal install sop-core --lib --package-env . will create the environment file .ghc.environment.xxx in the current folder, and the library will be available to ghc and ghci when they are invoked there.
Why isn't test1 available for test2?
Modern cabal makes a distinction between local packages and external packages.
local packages is the set of packages you are developing together in a project, being edited, recompiled and changed repeatedly. They are built "inplace" and not seen outside the project. They can depend on each other.
external packages are dependencies from build-depends: whose source code is downloaded from a package repository and which, when compiled, are put in the cabal store so that other Cabal projects might make use of them without re-compiling.
The list of local packages and other project-level configuration details are specified in a cabal.project file. But you don't need one if you work on a single isolated package; the default list of packages is simply ./*.cabal.
cabal wants to completely control the build environment of local packages, and will ignore the global environment file. In your case, you'll have to make test1 and test2 local packages in the same project (likely the best option) or publish test1 and treat it as an external package.
Note that "cabal project" is a concept relevant only during development. Packages are published independently, there are no "projects" in Hackage or other repositories, just packages.
What if I want to treat test1 as external without publishing it to Hackage?
You will have to set up a local package repository, basically a non-public Hackage.
You can tell Cabal about additional package repositories in the Cabal configuration file, that is, the file that configures cabal itself. Its location is given in the last line of cabal --help.
But how to set up the repository? The hackage-repo-tool can help with that.
Why did test3 fail? Why did further library installs fail?
That's weird, I have no idea why that happens. Did you by perchance delete the ~/.cabal folder between steps 3) and 5) ? What happens if you delete the global GHC environment file and try again?

Minimal cabal file for use in sandbox

I’m trying to write a project with the Hakyll library. In order to avoid messing up with my system, I’d install it in a cabal sandbox in the same folder where my Hakyll project lives.
Being more or less a beginner, I’m still struggling with getting the pest practices right. A simple approach would be to just do
$ cabal sandbox init
$ cabal install hakyll
$ cabal exec ghc -- --make site.hs
where the last line compiles my Hakyll generator using the libraries in the sandbox. The obvious disadvantage is that this is not reproducible. The main version of Hakyll could have change when I try to run it again from a clean checkout.
Another approach would be to write a proper project.cabal file (For example like this: chromaticleaves.cabal) and then do cabal install or cabal run.
However, I feel that this may be a bit too much information. As I do not intend to publish this project any more than needed, I’m not really convinced I need to put a project name and version number in there. (For example, in a Ruby Gemfile, I would also only specify the libraries and nothing else unless I wanted to publish a gem myself.)
So, eventually I figured that with a file like
$ cat project.cabal
cabal-version: >= 1.2
library
build-depends: base >=4.6
, containers
, process
, hakyll >=4.5
, pandoc
, pandoc-types
I can type
$ cabal sandbox init
$ cabal install --only-dependencies
$ cabal exec ghc -- --make site.hs
and it seems to download all dependencies and is able to compile the file.
Is this a reasonable approach or is the best practice really to give a full specification with name, version and executable sections in the cabal file?
Edit: Apparently, my approach does not let me do cabal repl. So either there exists a completely different way of doing it or it seems I have to go with a fuller specification.
I use your first approach myself for my Hakyll-based webpage. You don't need to create a .cabal file to pin the Hakyll version, you only need to add the following line to cabal.config:
constraints: hakyll == 4.5
I think that cabal repl will work with this approach, but you will need to load site.hs manually (:l site.hs). Or you can use cabal exec ghci -- site.hs.

cabal sandbox + haskeline

cabal sandbox init
cabal install haskeline
... installs successfully ...
ghci
Prelude> :module +System.Console.Haskeline
<no location info>:
Could not find module `System.Console.Haskeline'
ghc-pkg list haskeline
.. not found ..
What do I have to do get haskeline to work with cabal sandbox? If I install haskeline normally (no sandbox) it is fine (ghc-pkg list haskeline -- found it).
Either use cabal repl like Joseph mentioned or you can explicitly pass the package db to the GHCi shell relative to your current working directory.
ghci -no-user-package-db -package-db .cabal-sandbox/*-packages.conf.d YourModule.hs
It's recommended that you just use cabal.
In order to get ghci to use a local sandbox you must (a) set up a my-project.cabal file and (b) use cabal repl.

Resources