What exactly does stack runghc do? - haskell

A bit of a background that leads to the question I have.
I am learning Yesod, and I am reading the Yesod book. In the Basics section of the book, the instructions on how to get a basic Yesod application was given. The instructions basically says put this in a file:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Yesod
data HelloWorld = HelloWorld
mkYesod "HelloWorld" [parseRoutes|
/ HomeR GET
|]
instance Yesod HelloWorld
getHomeR :: Handler Html
getHomeR = defaultLayout [whamlet|Hello World!|]
main :: IO ()
main = warp 3000 HelloWorld
and run it with runhaskell helloworld.hs or stack runghc helloworld.hs. I did exactly this and that did not work. I get an error about yesod cannot be found. Apparently there is step I should have ran prior to this, which is not mentioned in the basic section. I found that step in the quickstart guide here https://www.yesodweb.com/page/quickstart
And the instruction in there basically says
install stack
Create a new scaffolded site: stack new my-project yesodweb/sqlite && cd my-project
Install the yesod command line tool: stack install yesod-bin --install-ghc
Build libraries: stack build
Launch devel server: stack exec -- yesod devel
View your Yesod site at http://localhost:3000/
I followed all of this and it seems to work, but now back to running the helloworld.hs file in the basic section. I understand above that the command stack install yesod-bin --install-ghc installs a yesod. So I thought that would fix the problem of Yesod not found. I went back to the directory where I had the helloworld.hs file and ran stack runghc helloworld.hs but that did not work.
It only worked when I moved helloworld.hs into the my-project project I created when I followed the quickstart guide. So my question now is, what exactly does the command stack runghc helloworld.hs and why is it that the command only works when I have the helloworld.hs file in a stack project?

Think of stack projects like node packages or python virtualenvs: they have their own dependencies that are separate from whatever you installed on your system. runhaskell (which does not go through stack) will use your global dependencies, but you didn't have yesod installed globally so it complained that it wasn't found. If you stack install yesod-bin inside a stack project, it will install it for that stack project only, so you can only use the dependencies if you're inside that project. For global installations, consider using cabal (note that cabal is horrific at package management and will error out on half the packages you try to install, so it's better to just stick with stack).

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.

But by using the -ddump-splices GHC option

I am learning yesod and would like to know, which code is generated behind, when I am using
mkYesod "HelloWorld" [parseRoutes|
/ HomeR GET
|]
function.
In the doc, it says:
We’ll look at this in more detail in the routing chapter. But by using
the -ddump-splices GHC option, we can get an immediate look at the
generated code.
How can I pass -ddump-splices to GHC option?
I start the application with:
stack runghc -ddump-splices helloworld.hs
I think you can use the OPTIONS_GHC pragma:
simply add
{-# OPTIONS_GHC -ddump-splices #-}
at the top of your file.
I don't see a way to pass options through to GHC when using stack runghc. You can compile your project with stack build, which accepts more options. The full command is stack build --ghc-options '-ddump-splices'.
After compiling, you may also want to run your project. If you followed the Yesod quickstart, I think stack exec -- yesod devel will work.
I ran across this today when working through the Yesod Book. The following seems to work well. Any option passed before -- is passed to stack and options after -- are passed directly to GHC
stack runghc -- -ddump-splices helloworld.hs

Could not find module ‘Test.QuickCheck’ on Windows

My ghci version is 8.4.3
I tried
stack install QuickCheck
Something was installed. But when I input import Test.QuickCheck, it tells Could not find module ‘Test.QuickCheck’ again. How can I fix it?
Firstly, stack install is not recommended for installing executables or libraries. Instead, there's a couple of things you can do to use the QuickCheck library:
If you want to use QuickCheck in a command such as stack ghci or stack ghc, you can add it as a --package option e.g. to run a REPL to play around with QuickCheck you can use stack ghci --package QuickCheck and then write import Test.QuickCheck.
If you want to write a small one-file program using QuickCheck, then you can run stack ghc --package QuickCheck -- MyProgram.hs (using the --package option from the last bullet point). Alternately, you can use stack's scripting functionality and include a line such as this at the top of your program:
-- stack --resolver lts-12.18 script --package QuickCheck
If you want to use QuickCheck in a large project, then add it as a dependency in your my-program.cabal or project.yaml file.
The same guidance applies to any package you may want to use.
myos>cabal update
myos>cabal install --lib QuickCheck
myos>ghci
gchi> import Test.QuickCheck

Force Haskell Stack build to use the Resolver Specified after Shebang

I'm trying to use haskell for scripting in the pattern specified here on the "Script interpreter" section:
https://haskell-lang.org/tutorial/stack-script
When I have comments like this in my script after the shebang:
#!/usr/bin/env stack
{- stack
--resolver nightly-2016-11-26
--install-ghc
runghc
--package http-conduit
-}
{-# LANGUAGE OverloadedStrings #-}
I know I can safely say run:
$ stack myfile.hs
And it correctly picks up the resolver version I specified I need.
However, if I also try to build my script, to have the best of both worlds in scripting for development while I figure things out, and compilation once I'm more sure...
username#myhost:~/$ stack exec -- ghc myfile.hs && ./myfile
Run from outside a project, using implicit global project config
Using resolver: lts-7.10 from implicit global project's config file: /home/username/.stack/global-project/stack.yaml
That will error out because it's using resolver 'lts-7.10' from my global config instead of 'nightly-2016-11-26' even though my comments after the shebang specify a specific, non-global resolver to use.
While I know I could run ghc above and explicitly call --resolver on the command line, I would prefer if this were picked up from what's within the file itself. Is this possible?

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.

Resources