Hspec unable to load interface for Spec file - haskell

I am trying to run hspec but getting error as "Failed to load interface for Spec file". I tried similar example from github, got same error. Please suggest where I am going wrong...(PS: I am able to run this with stack).
Example from github (https://github.com/FranklinChen/twenty-four-days2015-of-hackage)
I have following files:
-- src.HintExample.hs
module HintExample where
import SortWrapper (Sort)
import qualified Language.Haskell.Interpreter as I
import Language.Haskell.Interpreter (OptionVal((:=)))
-- | Dynamically load a 'Sort' implementation from a file.
-- src is needed to pick up our SortWrapper.
-- sort-plugins is a sample user plugins directory
loadSort :: I.MonadInterpreter m =>
String -- ^ module name
-> String -- ^ function name
-> m Sort
loadSort moduleName functionName = do
I.set [I.searchPath := ["src", "sort-plugins"]]
I.loadModules [moduleName]
I.setImports [moduleName, "SortWrapper"]
I.interpret (moduleName ++ "." ++ functionName) (I.as :: Sort)
And test file as
-- test.HintExampleSpec.hs
module HintExampleSpec where
import SortWrapper (Sort(Sort))
import HintExample (loadSort)
import qualified Language.Haskell.Interpreter as I
import Test.Hspec (Spec, hspec, describe, it, shouldBe)
import Test.Hspec.QuickCheck (prop)
-- | Required for auto-discovery.
spec :: Spec
spec =
describe "hint" $ do
it "dynamically loads a correct polymorphic sort function" $ do
Right (Sort ourSort) <-
I.runInterpreter (loadSort "OurSorter" "ourSort")
ourSort "ebcad" `shouldBe` "abcde"
ourSort [1 :: Int, 5, 4, 3, 7] `shouldBe` [1, 3, 4, 5, 7]
it "dynamically loads a wrong (only head) sort function" $ do
Right (Sort onlyHead) <-
I.runInterpreter (loadSort "OurSorter" "onlyHead")
onlyHead "ebcad" `shouldBe` "e"
onlyHead [True, False] `shouldBe` [True]
main :: IO ()
main = hspec spec
Spec file (Spec.hs) in test directory as:
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
running as:
runhaskell test/Spec.hs

Related

how to list the functions exported by a Haskell module from an .hs script?

I am aware of this thread and the agreed-upon ghci :browse command, but I am looking for something similar to run from a script.hs file:
Say I have a module that I can import into my script.hs. How do I then view the list of functions I have just gained access to?
What I've settled on for now
Adapting this thread that suggests the now-deprecated ghc-mod command-line program, I am
calling the terminal command ghc -e ':browse <module, e.g. Data.List>'
from my script.hs using Shelly.
My full script:
#!/usr/bin/env runghc
{-# LANGUAGE OverloadedStrings #-}
import Safe (headDef)
import Shelly
import System.Environment (getArgs)
import qualified Data.Text as T
mdl :: IO String
mdl = getArgs >>= return . headDef "Data.List"
runShelly :: String -> IO ()
runShelly mdl = shelly $ silently $ do
out <- run "ghc" ["-e", T.pack (":browse " ++ mdl)]
let lns = T.lines out
liftIO $ mapM_ (putStrLn .T.unpack) $ lns
main :: IO ()
main = mdl >>= runShelly
This way I can pass the module name on the command line as <script> <module> and get back the functions, one per line. It defaults to Data.List if I pass no arguments.
So that's a solution, but surely there must be handier introspection facilities than this?

Couldn't match type ‘[Char]’ with ‘Data.Text.Internal.Text’

I am trying to figure out, how to build JSON in Haskell with the following example:
module Main where
import GHC.Exts
import Data.Aeson
import qualified Data.Text.Lazy.IO as T
import qualified Data.Text.Lazy.Encoding as T
val :: Value
val = Object $ fromList [
("numbers", Array $ fromList [Number 1, Number 2, Number 3]),
("boolean", Bool True) ]
main :: IO ()
main = T.putStrLn . T.decodeUtf8 . encode $ val
When I tried to compile, the compiler complains:
• Couldn't match type ‘[Char]’ with ‘Data.Text.Internal.Text’
Expected type: Item Object
Actual type: ([Char], Value)
• In the expression: ("boolean", Bool True)
In the first argument of ‘fromList’, namely
‘[("numbers", Array $ fromList [Number 1, Number 2, ....]),
("boolean", Bool True)]’
In the second argument of ‘($)’, namely
‘fromList
[("numbers", Array $ fromList [Number 1, Number 2, ....]),
("boolean", Bool True)]’
|
12 | ("boolean", Bool True) ]
| ^^^^^^^^^^^^^^^^^^^^^^
You need to put OverloadedStrings extension on top of the file:
#!/usr/bin/env stack
-- stack script --resolver lts-12.7
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.Text.Lazy.Encoding as T
import qualified Data.Text.Lazy.IO as T
import GHC.Exts
val :: Value
val =
Object $
fromList
[ ("numbers", Array $ fromList [Number 1, Number 2, Number 3])
, ("boolean", Bool True)
]
main :: IO ()
main = T.putStrLn . T.decodeUtf8 . encode $ val
And on executing them:
$ stack fuse.hs
{"boolean":true,"numbers":[1,2,3]}
To understand the reasons on why that makes it work, refer to this answer.
The error you get is about wrong string representation. Haskell has many of them, e.g. ByteString (strict or lazy), Text (strict or lazy), String (alias for [Char]). The last one is the default and also the one you should avoid most of the time.
Aeson library uses the Text as string representation. You can fix your code using T.pack before the string (to convert it to Text), like this:
val :: Value
val =
Object $
fromList
[ (T.pack "numbers", Array $ fromList [Number 1, Number 2, Number 3])
, (T.pack "boolean", Bool True)
]
Or you can just enable OverloadedStrings extension. When it's on then Haskell compiler will try to figure out which string representation it should use. Just put {-# LANGUAGE OverloadedStrings #-} at the top of your file and it should work.

Top-level expression evaluation at compile time

Is there any way to ensure, that an expression like the following would be evaluated at compile time?
myList :: [Int]
myList = sort [3,2,0,1]
If what you're evaluating is an instance of Lift, you can evaluate it at compile time using TemplateHaskell:
{-# LANGUAGE TemplateHaskell #-}
module Sort where
import Data.List
import Language.Haskell.TH.Syntax
myList :: [Int]
myList = $(lift (sort [3,2,0,1] :: [Int]))
If you want, you can check what it has compiled to with -ddump-splices:
$ ghc -ddump-splices sort
[1 of 1] Compiling Sort ( sort.hs, sort.o )
sort.hs:9:12-41: Splicing expression
lift (sort [3, 2, 0, 1] :: [Int]) ======> [0, 1, 2, 3]

Hspec: discovery, custom main, and passing argument to spec

I am trying to use hspec-discover along with custom Main. Custom Main is a bracket that creates a file descriptor to be used by all Spec's.
This is my Spec.hs:
{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
This is my Main.hs:
module Main (main) where
import Control.Exception
import System.Posix.IO
import System.Posix.Files
import Test.Hspec
import Spec (spec)
main :: IO ()
main = bracket
(openFd verybigFile ReadWrite (Just 384) defaultFileFlags)
(\fd -> closeFd fd >> removeLink verybigFile)
(\fd -> hspec (spec fd))
where
verybigFile = "test/verybigFile"
For my spec in individual autodiscovered module to accept the file descriptor argument, I need to declare it as
spec :: Fd -> Spec
but hspec-discover demands that spec is declared as
spec :: Spec
otherwise autogenerated module does not compile:
test/Spec.hs:8:68:
Couldn't match type `System.Posix.Types.Fd -> Spec'
with `hspec-core-2.1.7:Test.Hspec.Core.Spec.Monad.SpecM () ()'
Expected type: hspec-core-2.1.7:Test.Hspec.Core.Spec.Monad.SpecWith
()
Actual type: System.Posix.Types.Fd -> Spec
In the second argument of `describe', namely `SendfileSpec.spec'
In the second argument of `postProcessSpec', namely
`(describe "Sendfile" SendfileSpec.spec)'
In the expression:
postProcessSpec
"test/SendfileSpec.hs" (describe "Sendfile" SendfileSpec.spec)
So, how to pass an argument to the spec without disturbing autodiscovery? My imagination drifts towards IORef's but the idea makes me shudder. What would be a Right Way to do it?
Sharing values across spec files is currently not support with hspec-discover. But you can still share values within the same spec file. The following works:
FooSpec.hs:
module FooSpec (spec) where
import Test.Hspec
import System.IO
spec :: Spec
spec = beforeAll (openFile "foo.txt" ReadMode) $ afterAll hClose $ do
describe "hGetLine" $ do
it "reads a line" $ \h -> do
hGetLine h `shouldReturn` "foo"
it "reads an other line" $ \h -> do
hGetLine h `shouldReturn` "bar"
Spec.hs:
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
But note that beforeAll is generally considered a code smell. It's good practice to use before instead if possible.

Template Haskell and Implicit Parameters

Is there a way to create functions with implicit parameters or let bindings with implicit parameters using template haskell?
I.e. is it possible to generate a signature like this using template haskell:
doSomething :: (?context :: Context) => m a
Or an invocation like this:
invoc = let ?context = newContext in doSomething
I could not find suitable algebraic data types nor any functions which would help me out on this topic in the API documentation for template haskell. I'm using GHC 7.4.2.
If there is no native support for this extension in template haskell, is there some other possibility to inject code during compilation (maybe something like a general “code injection function” within template haskell?).
EDIT: I tried the suggestion from the comments, this is what happens:
runQ [d| f :: (?c :: String) => Int ; f = 7 |]
<interactive>:10:17: parse error on input `c'
whereas this works:
runQ [d| f :: Int ; f = 7|]
[SigD f_0 (ConT GHC.Types.Int),ValD (VarP f_0) (NormalB (LitE (IntegerL 7))) []]
doesn't seem to be supported.
Here's one way that's pretty fragile, but sort of works. While you can't refer
to ?x in the Exp that template haskell uses, you can refer to a definition in
another module like:
reserved_prefix_x = ?x
Below is some code that generates variables like above in one run of ghc,
and in a second run of ghc the variables actually refer to implicit parameters.
{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}
module GenMod (h) where
import Data.Generics
import Data.IORef
import Data.List
import Language.Haskell.Meta.Parse as P
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import qualified Data.Set as S
import qualified Language.Haskell.Exts.QQ as Q
import System.IO.Unsafe
h = Q.hs { quoteExp = \s -> do
r <- either fail (upVars . return) (P.parseExp s)
writeMod'
return r
}
pfx = "q_"
{-# NOINLINE vars #-}
vars :: IORef (S.Set String)
vars = unsafePerformIO (newIORef S.empty)
writeMod' = runIO $ writeFile "GEN.hs" . ppMod =<< readIORef vars
writeMod = -- might be needed to avoid multiple calls to writeFile?
-- in this example this is called for every use of `h'
QuasiQuoter { quoteDec = \ _ -> do
writeMod'
[d| _ = () |] }
ppMod xs = "{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}\n\
\module GEN where\n" ++
unlines (map (\x -> pfx ++ x ++ " = ?" ++ x) (S.toList xs))
upVars x = do
x' <- x
runIO $ modifyIORef vars (S.union (getMatchingVars x'))
runIO $ print =<< readIORef vars
return x'
getMatchingVars =
everything
S.union
(mkQ S.empty
(\ (OccName x) -> maybe S.empty S.singleton (stripPrefix pfx x)))
A Main.hs file that uses the quasiquoter GenMod.hs:
{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams, QuasiQuotes, TemplateHaskell, CPP #-}
import GenMod
#ifndef stage1
import GEN
#endif
f_ = [h| q_hithere |]
You have to call ghc twice, like:
ghci -Dstage1 Main.hs
GHCi, version 7.6.1: http://www.haskell.org/ghc/ :? for help
[1 of 2] Compiling GenMod ( GenMod.hs, interpreted )
[2 of 2] Compiling Main ( Ex.hs, interpreted )
fromList ["hithere"]
Ex.hs:8:6: Not in scope: `q_hithere'
Failed, modules loaded: GenMod.
Though ghc fails, it still generates the GEN.hs which contains:
{-# LANGUAGE NoMonomorphismRestriction, ImplicitParams #-}
module GEN where
q_hithere = ?hithere
Which will be there when you load Main (leaving out the -D flag)
*Main> :t f_
f_ :: (?hithere::t) => t
This kind of trouble probably isn't worth it. Maybe other situations of calling out to other programs from TH are more motivating such as inline calls to other languages http://hpaste.org/50837 (gfortran example)
Since I used haskell-src-meta's default parser, the quasiquote gets to use variables "reserved_prefix_x" not "?x". It should be possible to accept the "?x" without too much difficulty.

Resources