I am learning Haskell and trying to build an example from the book.
When I load the code using ":l BetterPredicate" command in GHCI, I am getting following error:
Prelude> :l BetterPredicate
[1 of 2] Compiling RecursiveContents ( RecursiveContents.hs, interpreted )
RecursiveContents.hs:12:32: warning: [-Wtabs]
Tab character found here.
Please use spaces instead.
[2 of 2] Compiling Main ( BetterPredicate.hs, interpreted )
BetterPredicate.hs:3:1: error:
Failed to load interface for ‘System.Time’
Perhaps you meant
System.CPUTime (from base-4.9.1.0)
System.Cmd (from process-1.4.3.0)
System.Mem (from base-4.9.1.0)
Use -v to see a list of the files searched for.
Failed, modules loaded: RecursiveContents.
Here is the code I am trying to compile:
import Control.Monad (filterM)
import System.Directory (Permissions (..), getModificationTime, getPermissions)
import System.Time (ClockTime(..))
import System.FilePath (takeExtension)
import Control.Exception (bracket, handle)
import System.IO (IOMode(..), hClose, hFileSize, openFile)
-- Our functions
import RecursiveContents (getRecursiveContents)
type Predicate = FilePath -- Path to directory entry
-> Permissions -- permissions
-> Maybe Integer -- file size (Nothing if not file)
-> ClockTime -- last modified
-> Bool
-- TBD
getFileSize :: FilePath -> IO (Maybe Integer)
betterFind :: Predicate -> FilePath -> IO [FilePath]
betterFind p path = getRecursiveContents >>= filterM check
where check name = do
perms <- getPermissions name
size <- getFileSize name
modified <- getModificationTime name
return (p name perms size modified)
simpleFileSize :: FilePath -> IO Integer
simpleFileSize path = do
h <- openFile path ReadMode
size <- hFileSize h
hClose
return size
saferFileSize :: FilePath -> IO (Maybe Integer)
saferFileSize path = handle (\_ -> return Nothing) $ do
h <- openFile path ReadMode
size <- hFileSize h
hClose
return (Just size)
getFileSize :: FilePath -> IO (Maybe Integer)
getFileSize path = handle (\_ -> return Nothing) $
bracket (openFile path ReadOnly) hClose $ \h -> do
size <- hFileSize h
hClose
return (Just size)
type InfoP a = FilePath -- path to directory entry
-> Permissions -- permissions
-> Maybe Integer -- file size (Nothing if not file)
-> ClockTime -- last modified
-> a
pathP :: InfoP FilePath
pathP path _ _ _ = path
sizeP :: InfoP Integer
sizeP _ _ (Just size) _ = size
sizeP _ _ Nothing _ = -1
equalP :: (Eq a) => InfoP a -> a -> InfoP Bool
--equalP f k = \w x y z -> f w x y z == k
equalP f k w x y z = f w x y z == k
According to the documentation: http://hackage.haskell.org/package/old-time-1.1.0.3/docs/System-Time.html this module is part of old-time library, so I suppose I need to import it somehow, but I am not sure how to do it if I am not building a package using Cabal (there is no *.cabal file) or something else, I just want to use my code in GHCI.
Just use cabal install to install a package globally
$ cabal install old-time
It will give you a warning, but that's ok if you are just using it for ghci support.
Also note the documentation warns:
This library is deprecated, please look at Data.Time in the time package instead.
Related
I am trying to fix and run every example on the Real World Haskell book and learn something in the process and I got stuck at chapter 9. By reading the comments I got the following code to compile:
FoldDir.hs:
import ControlledVisit
import Data.Char (toLower)
import Data.Time.Clock (UTCTime(..))
import System.Directory (Permissions(..))
import System.FilePath ((</>), takeExtension, takeFileName)
data Iterate seed
= Done { unwrap :: seed }
| Skip { unwrap :: seed }
| Continue { unwrap :: seed }
deriving (Show)
type Iterator seed = seed -> Info -> Iterate seed
foldTree :: Iterator a -> a -> FilePath -> IO a
foldTree iter initSeed path = do
endSeed <- fold initSeed path
return (unwrap endSeed)
where
fold seed subpath = getUsefulContents subpath >>= walk seed
walk seed (name : names) = do
let path' = path </> name
info <- getInfo path'
case iter seed info of
done # (Done _) -> return done
Skip seed' -> walk seed' names
Continue seed'
| isDirectory info -> do
next <- fold seed' path'
case next of
done # (Done _) -> return done
seed'' -> walk (unwrap seed'') names
| otherwise -> walk seed' names
walk seed _ = return (Continue seed)
atMostThreePictures :: Iterator [FilePath]
atMostThreePictures paths info
| length paths == 3
= Done paths
| isDirectory info && takeFileName path == ".svn"
= Skip paths
| extension `elem` [".jpg", ".png"]
= Continue (path : paths)
| otherwise
= Continue paths
where
extension = map toLower (takeExtension path)
path = infoPath info
countDirectories count info =
Continue (if isDirectory info then count + 1 else count)
ControlledVisit.hs:
module ControlledVisit where
import Control.Monad (forM, liftM)
import Data.Time.Clock (UTCTime(..))
import System.FilePath ((</>))
import System.Directory
( Permissions(..)
, getModificationTime
, getPermissions
, getDirectoryContents
)
import Control.Exception
( bracket
, handle
, SomeException(..)
)
import System.IO
( IOMode(..)
, hClose
, hFileSize
, openFile
)
data Info = Info
{ infoPath :: FilePath
, infoPerms :: Maybe Permissions
, infoSize :: Maybe Integer
, infoModTime :: Maybe UTCTime
} deriving (Eq, Ord, Show)
getInfo :: FilePath -> IO Info
getInfo path = do
perms <- maybeIO (getPermissions path)
size <- maybeIO (bracket (openFile path ReadMode) hClose hFileSize)
modified <- maybeIO (getModificationTime path)
return (Info path perms size modified)
traverseDirs :: ([Info] -> [Info]) -> FilePath -> IO [Info]
traverseDirs order path = do
names <- getUsefulContents path
contents <- mapM getInfo (path : map (path </>) names)
liftM concat $ forM (order contents) $ \ info -> do
if isDirectory info && infoPath info /= path
then traverseDirs order (infoPath info)
else return [info]
getUsefulContents :: FilePath -> IO [String]
getUsefulContents path = do
names <- getDirectoryContents path
return (filter (`notElem` [".", ".."]) names)
isDirectory :: Info -> Bool
isDirectory = maybe False searchable . infoPerms
maybeIO :: IO a -> IO (Maybe a)
maybeIO act = handle (\ (SomeException _) -> return Nothing) (Just `liftM` act)
traverseVerbose order path = do
names <- getDirectoryContents path
let usefulNames = filter (`notElem` [".", ".."]) names
contents <- mapM getEntryName ("" : usefulNames)
recursiveContents <- mapM recurse (order contents)
return (concat recursiveContents)
where
getEntryName name = getInfo (path </> name)
isDirectory info = case infoPerms info of
Nothing -> False
Just perms -> searchable perms
recurse info = do
if isDirectory info && infoPath info /= path
then traverseVerbose order (infoPath info)
else return [info]
But when I try to run it in GHCi as explained in the book it fails with a weird error that as far as I understand is about GHCi itself:
Prelude> :l FoldDir.hs
[1 of 2] Compiling ControlledVisit ( ControlledVisit.hs, interpreted )
[2 of 2] Compiling Main ( FoldDir.hs, interpreted )
Ok, two modules loaded.
*Main> foldTree atMostThreePictures []
<interactive>:2:1: error:
• No instance for (Show (FilePath -> IO [FilePath]))
arising from a use of ‘print’
(maybe you haven't applied a function to enough arguments?)
• In a stmt of an interactive GHCi command: print it
I think I understand the No instance for (Show (FilePath -> IO [FilePath])) part but I am clueless about the print it. I know it is a special variable in GHCi that stores the evaluation result of the last expression and I guess the code is trying to print a function or a monad, but I don't get where it is happening.
As simple as possible - signature of Your function foldTree is:
foldTree :: Iterator a -> a -> FilePath -> IO a
You are supplying it with two arguments, one of type Iterator [FilePath] and second of type FilePath. Due to default partial application such call returns function with signature:
FilePath -> IO [FilePath]
GHCI wants to display the result of Your call but it cannot, as this type has no defined instance of typeclass Show. And so, it gives You an error telling exactly this.
I have a module called EditorTest in a similarly-named file.
It imports some modules. It also has some declarations in it, as any Haskell module does.
I'd like to programmatically obtain a list of the top level available declarations within the context of that module.
What I have so far is a way to get the top level local declarations of this module only, by using hint. That's fine, and I figure I could recurse the imports (and so on into those modules, etc), collecting a list of them, then use hint to grab all the available declaration names... but if possible, I'm after an easier way to find the available declarations in a given module. I figure Haskell's API should have some way to do that.
Is there such a thing?
Self-answering because it's been a while and I spiked out a proof of concept ages ago, so I'll put it here for others, even if it is fairly messy.
So, assuming we have this file as TestModule.hs:
module TestModule (g, h) where
import Data.List as L
f = head
g = f [f]
h = L.map
Then the following code can be used to show how we can pull out the top level declarations and use them by using Hint's Language.Haskell.Interpreter module and the Language.Haskell.Exts.SrcLoc module from the haskell-src-exts package:
import Data.List
import Control.Monad
import qualified Language.Haskell.Interpreter as I
import Language.Haskell.Interpreter (Interpreter, GhcError(..), InterpreterError(..))
import qualified Language.Haskell.Exts as H
import Language.Haskell.Exts.SrcLoc
main :: IO ()
main = do r <- I.runInterpreter interpreterTest
case r of
Left err -> putStrLn $ errorString err
Right () -> return ()
errorString :: InterpreterError -> String
errorString (WontCompile es) = intercalate "\n" (header : map unbox es)
where
header = "ERROR: Won't compile:"
unbox (GhcError e) = e
errorString e = show e
p :: String -> Interpreter ()
p = I.liftIO . putStrLn
emptyLine :: Interpreter ()
emptyLine = p ""
interpreterTest :: Interpreter ()
interpreterTest =
do
p "Finding out what the module exports are."
p "To do this, we grab the text of the src/TestModule.hs"
moduleContents <- I.liftIO . readFile $ "src/TestModule.hs"
emptyLine
p "Next, we find what the import module names are, so we can load them"
p "To do that, we'll parse the contents of src/TestModule.hs as AST"
let
parseResult = H.parseModule moduleContents
parsedModuleDeclE =
case parseResult of
H.ParseOk parsed -> Right parsed
H.ParseFailed _ errorReason -> Left errorReason
(moduleDeclarations, moduleImports) =
case parsedModuleDeclE of
Left _ ->
([], [])
Right (H.Module _ _ _ _ _ importDecls decls) -> (decls, importDecls)
p $ show moduleImports
emptyLine
p "Now we can pull the module names out of the ImportDecl values:"
let
moduleNames =
do
H.ModuleName s <- fmap H.importModule moduleImports
return s
p $ show moduleNames
emptyLine
p "After this we can obtain a list of the module exports for each of these modules and join them together:"
I.loadModules ["src/TestModule.hs"]
I.setTopLevelModules ["TestModule"]
topLevelImportDecls <- fmap concat $ mapM I.getModuleExports moduleNames
-- p $ show $ do { I.Fun f <- topLevelImportDecls ; return f } -- filter for functions only
p $ show $ fmap I.name topLevelImportDecls
emptyLine
p "Then, we can get the top level declarations of the initial module and add them, too:"
-- p $ show moduleDeclarations -- AST in haskell types
let
localDeclaractions =
do
(H.PatBind _ (H.PVar (H.Ident declName)) _ _) <- moduleDeclarations
return declName
availableDeclarations =
localDeclaractions ++ fmap I.name topLevelImportDecls
p $ show localDeclaractions
emptyLine
p "Finally, we have our big list:"
p $ show availableDeclarations
emptyLine
p "Now, what about we get all of their types, just for fun?"
typesOfAll <- sequence $ fmap (\n -> fmap ((n ++ " :: ") ++) (I.typeOf n)) availableDeclarations
p $ Data.List.intercalate "\n" typesOfAll
return ()
I have a
foobar :: IO (ParseResult [(String,String)])
ParseResult is a monad defined here: https://hackage.haskell.org/package/haskell-src-exts-1.13.5/docs/Language-Haskell-Exts-Parser.html#t:ParseResult
I want to take those strings and write them to a LaTeXT m () defined in https://hackage.haskell.org/package/HaTeX-3.17.1.0/docs/Text-LaTeX-Base-Writer.html
Running this function results in no file being created.
writeReport2 :: [Char] -> IO (ParseResult (IO ()))
writeReport2 name = do x <- foobar
return $ do y <- x
return $ do z <- (execLaTeXT.docAndGraph) y
renderFile fileName z
where
fileName = name ++ ".tex"
However the code:
writeReport :: t -> LaTeXT IO a -> IO ()
writeReport name report = createLatex >>= renderFile fileName
where
createLatex = execLaTeXT report
fileName = "AAAAA" ++ ".tex"
testFoo = [(" | HaskellExample Example File\n | Two examples are given below:\n\n >>> fib 10\n 55\n\n >>> putStrLn \"foo\\nbar\"\n foo\n bar ","fib :: Int -> Int"),("\n | This is a thing: ","fib = undefined"),("\n | This is a thing:\n","fibar :: String -> Float")]
itWorks = writeReport "AAAA.txt" $ docAndGraph testFoo
Will create a new file.
Both sets of code type check.
I could get writeReport2 working without modification.
I think what might have been your problem is the nested IO action in the return value of writeResport2!
In order to flatten the nested IO actions, I had to use the function join :: Monad m => m (m a) -> m a from Control.Monad:
main :: IO ()
main = join $ fromParseResult <$> writeReport2 "test"
Here is my complete code:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Language.Haskell.Exts.Parser
import Text.LaTeX.Base.Writer
import Text.LaTeX
import Data.String
import Control.Monad
foobar :: IO (ParseResult [(String, String)])
foobar = return (ParseOk testFoo)
testFoo = [ ( " | HaskellExample Example File\n | Two examples are given below:\n\n >>> fib 10\n 55\n\n >>> putStrLn \"foo\\nbar\"\n foo\n bar "
, "fib :: Int -> Int"
)
, ("\n | This is a thing: ", "fib = undefined")
, ("\n | This is a thing:\n", "fibar :: String -> Float")
]
docAndGraph :: Monad m => [(String, String)] -> LaTeXT m ()
docAndGraph x = do
documentclass [] article
document $
raw (fromString (show x))
writeReport2 :: [Char] -> IO (ParseResult (IO ()))
writeReport2 name = do
x <- foobar
return $ do
y <- x
return $ do
z <- (execLaTeXT . docAndGraph) y
renderFile fileName z
where
fileName = name ++ ".tex"
main :: IO ()
main = join $ fromParseResult <$> writeReport2 "test"
Loading into GHCi:
$ stack ghci
io-action-nested-in-other-monads-not-executing-0.1.0.0: initial-build-steps (exe)
Configuring GHCi with the following packages: io-action-nested-in-other-monads-not-executing
Using main module: 1. Package `io-action-nested-in-other-monads-not-executing' component exe:io-action-nested-in-other-monads-not-executing with main-is file: /home/sven/dev/stackoverflow-questions/io-action-nested-in-other-monads-not-executing/src/Main.hs
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/sven/.ghc/ghci.conf
[1 of 1] Compiling Main ( /home/sven/dev/stackoverflow-questions/io-action-nested-in-other-monads-not-executing/src/Main.hs, interpreted )
Ok, modules loaded: Main.
Loaded GHCi configuration from /tmp/ghci22616/ghci-script
And running it:
λ main
Creates this file:
$ cat test.tex
\documentclass{article}\begin{document}[(" | HaskellExample Example File\n | Two examples are given below:\n\n >>> fib 10\n 55\n\n >>> putStrLn \"foo\\nbar\"\n foo\n bar ","fib :: Int -> Int"),("\n | This is a thing: ","fib = undefined"),("\n | This is a thing:\n","fibar :: String -> Float")]\end{document}%
I know it is not the scope of the question, but you could circumvent the nested IO if you want, by doinf this, for example:
writeReport3 :: [Char] -> IO ()
writeReport3 name = do
let fileName = name ++ ".tex"
x <- foobar
case x of
ParseOk y -> do
z <- execLaTeXT (docAndGraph y)
renderFile fileName z
ParseFailed _ _ ->
return ()
main :: IO ()
main = writeReport3 "test"
I try to open encrypted zip file and then to write it again. Unfortunately I keep getting "Read error" and don't understand why. I find the documentation of LibZip for Haskell hard to follow so would be grateful for any explanation on how it works. Here is my code:
writeZip :: FilePath -> [(FilePath, ZipSource)] -> IO ()
writeZip zipName zipContent = withArchive [CreateFlag] zipName $ do
mapM_ (uncurry addFile) zipContent
readEncryptedZip :: FilePath -> Password -> IO [(FilePath, ZipSource)]
readEncryptedZip zipName passwd = withEncryptedArchive [CheckConsFlag]
passwd zipName $ do
nn <- fileNames []
ss <- mapM (\n -> sourceFile n 0 (-1)) nn
return $ zip nn ss
and main module:
main = readEncryptedZip "protected_file2.zip" "ll" >>= writeZip "unprotected.zip"
Using Codec.Archive.LibZip, ghci version 8.0.1, MacOs Sierra 10.12
Thanks in advance!
As title states, I am trying to find a given string within a given path. Here is what I come up so far:
getRecursiveContents :: FilePath -> IO [FilePath]
getRecursiveContents topdir = do
names <- getDirectoryContents topdir
let properNames = filter (`notElem` [".", ".."]) names
paths <- forM properNames $ \name -> do
let path = topdir </> name
isDirectory <- doesDirectoryExist path
if isDirectory
then getRecursiveContents path
else return [path]
return (concat paths)
findInFile:: String -> FilePath -> IO Bool
findInFile needle filePath= do
content <- readFile filePath
return (needle `L.isInfixOf` content)
findInFolder:: (String -> Bool) -> FilePath -> String -> IO [IO Bool]
findInFolder p path needle = do
files <- getRecursiveContents path
return (map (findInFile needle) (filter p files))
find = findInFolder (\p -> takeExtension p `elem` [".py", ".xml", ".html"])
I can :
*Main> findInFile "search_string" "./path/to/a/file"
True
Which is perfect but I cannot do the same search for a folder:
*Main> find "./path/to/a/folder" "search_string"
*Main>
In my file system ./path/to/a/file is located under ./path/to/a/folder. Thus I was expecting the same result.
What am I doing wrong?
Note: getRecursiveContents is from real world haskell.
It does indeed work. The only issue is with how things are printed. When you type some expressions into ghci, it will call print on that expression. If the value has type IO x, it will execute the IO action and print x only if it has a Show instance; otherwise it prints no additional information.
find "./path/to/a/folder" "search_string" produces a list of IO actions, which have no Show instance. You can get the result of find, which is again a list of IO actions, and then execute them:
> x <- find "./path/to/a/folder" "search_string"
> sequence x
> [True, False ...
Likely you wanted to do this originally in your function. Simply make the following changes:
findInFolder:: (String -> Bool) -> FilePath -> String -> IO [Bool]
findInFolder p path needle = do
files <- getRecursiveContents path
mapM (findInFile needle) (filter p files)
Now findInFolder will work as you expect.