shake build: read filepath list from config file - haskell

I have a config.cfg file where a the variable file_list is a list of relative path to files
file_list = file1 dir1/file2 ../dir2/file3
How do I read this variable in to get a file_list::[FilePath]?
Tried to follow the Development.Shake.Config API Doc without success. I need something to achieve that
file_list <- getConfig "file_list"
let fl = ??? file_list
need fl
ps. I'am an Haskell beginner

The type of file_list is Maybe String, and the type of fl needs to be [FilePath], so the question becomes how to write a function to transform between the two. One option is:
let fl = words (fromMaybe "" file_list)
The fromMaybe function replaces Nothing with "" - so you now have a String. The words function splits a string on the spaces, to produce [String]. In Haskell FilePath is a synonym for String so it all works out.
If instead you want to error out if the key is missing, you can do:
Just file_list <- getConfig "file_list"
let fl = words file_list
need fl
Now you are asserting and unwrapping the Maybe in file_list, so if it is Nothing you get a runtime crash, and if it is Just you get it without the Just wrapper, so can simply use words.

Related

Use only Integers of a file with text

My function works ok. But I want to use this function with a file's text. The text file has a word before an integer list. How can I do this?
This is the function:
broke :: Integer -> Integer
broke n = pollard 1 2 n 2 2
The contents of the file is:
Word (11,12)
I want to apply the function broke to the first number.
Well this might be kind of a cheat, but the contents of that file is a valid Haskell expression so you could use Read to do it:
import System.IO (readFile)
data Word = Word (Integer,Integer)
deriving (Read)
main = do
contents <- readFile "path/to/file" -- or wherever your file is
let Word (x,y) = read contents
print $ broke x
The reason this works is that deriving (Read) automatically writes a parser for you, so you get the function read :: String -> Word for free. So this technique is only going to work for files whose contents look like Haskell -- otherwise you will need to write your own parser.

Flexible number of arguments to haskell program

I am using the System.FilePath.Find module of filemanip to recursively find all files I need to process (here I will be using just printing to console as the action to perform, in order not confuse things). Now, this code:
import System.Environment (getArgs)
import System.FilePath (FilePath)
import System.Directory (doesDirectoryExist, getDirectoryContents,doesFileExist)
import Control.Monad
import System.FilePath.Find (find,always,fileType,(==?),FileType(..),(&&?),extension)
main= do
[dbFile,input]<- getArgs
files <- findFiles input
mapM_ putStrLn files
return ()
searchExtension :: String
searchExtension = ".hs"
findFiles :: FilePath -> IO [String]
findFiles = find (always) ( fileType ==? RegularFile &&? extension ==? searchExtension)
works well with this call
./myprog tet .
In this case, the get argument is ignored (will be the output database file later) and the second argument is searched recursively for matching files. It also allows me to specify just a single file, which is just perfect!
BUT, I would like to be able to specify
./myprog tet path1 path2 path4 file1
but this of course fails in the pattern matching:
./myprog tet . .
myprogt: user error (Pattern match failure in do expression at myprog.hs:11:9-22)
Now, how do I make this program more flexible, so that I can take more than two arguments?
Sorry for asking this, actually, but my Haskell knowledge is limited but increasing for every new thing I have to do in my first project.
Well, you can use a different pattern like:
(dbFile:inputs) <- getArgs
where dbFile will match the first argument passed while inputs will match any number of file names (even 0. If you want at least one path name use inputs#(_:_) instead of the simple inputs).
Then you can use mapM to call findFiles for each path in inputs:
files <- mapM findFiles input
mapM_ putStrLn $ concat files
Instead of mapM you could modify findFiles to accept a [FilePath] argument instead of a simple FilePath.
Note that to parse command arguments you could consider using some module like getopt. You should also read this page about argument handling.

Haskell write a list in file and read later

I am trying to write a list into a file and later on I want to read the file contents into the list as well.
So I have a list like this ["ABC","DEF"]
I have tried things like
hPrint fileHandle listName
This just prints into file "["ABC","DEF"]"
I have tried unlines but that is priniting like "ABC\nDEF\n"
Now in both the cases, I cant read back into proper list. The output file has quotes and because of which when I read, I get like this ["["ABC","DEF"]""] i.e a single string in list.
As I am not succeeding in this, I tried to write the list line by line, I tried to apply a map and the function to write the list k = map (\x -> hPrint fileSLC x) fieldsBefore, it is not doing anything, file is blank. I think if I write everything in separate line, I will be able to read like (lines src) later on.
I know whatever I am doing is wrong but I am writing the code on Haskell for second time only, last time I just a wrote a very a small file reading program. Moving from imperative to functional is not that easy. :(
Try using hPutStrLn and unlines instead of hPrint. The hPrint internally calls show which causes Strings to be quoted and escaped.
hPutStr fileHandle (unlines listName)
Alternatively, use a mapM or a forM. A verbose example is:
forM_ listName $ \string ->
hPutStrLn string
This can be simplified ("eta-contracted", in lambda-calculus terminology) to
forM_ listName hPutStrLn
As you have seen, when you read from a file, you get a String. In order to convert this String into a list, you will need to parse it.
For k = map (\x -> hPrint fileSLC x) fieldsBefore to work, you need to use mapM or mapM_ instead of map.

How to search a pattern from a file/String in Haskell

** old**
Suppose we have a pattern ex. "1101000111001110".
Now I have a pattern to be searched ex. "1101". I am new to Haskell world, I am trying it at my end. I am able to do it in c but need to do it in Haskell.
Given Pattern := "1101000111001110"
Pattern To Be Searched :- "110
Desired Output:-"Pattern Found"`
** New**
import Data.List (isInfixOf)
main = do x <- readFile "read.txt"
putStr x
isSubb :: [Char] -> [Char] -> Bool
isSubb sub str = isInfixOf sub str
This code reads a file named "read", which contains the following string 110100001101. Using isInfixOf you can check the pattern "1101" in the string and result will be True.
But the problem is i am not able to search "1101" in the string present in "read.txt".
I need to compare the "read.txt" string with the user provided string. i.e
one string is their in the file "read.txt"
and second string user will provid (user defined) and we will perform search and find whether user defined string is present in the string present in "read.txt"
Answer to new:
To achieve this, you have to use readLn:
sub <- readLn
readLn accepts input until a \n is encountered and <- binds the result to sub. Watch out that if the input should be a string you have to explicitly type the "s around your string.
Alternatively if you do not feel like typing the quotation marks every time, you can use getLine in place of readLn which has the type IO String which becomes String after being bound to sub
For further information on all functions included in the standard libraries of Haskell see Hoogle. Using Hoogle you can search functions by various criteria and will often find functions which suit your needs.
Answer to old:
Use the isInfixOf function from Data.List to search for the pattern:
import Data.List (isInfixOf)
isInfixOf "1101" "1101000111001110" -- outputs true
It returns true if the first sequence exists in the second and false otherwise.
To read a file and get its contents use readFile:
contents <- readFile "filename.txt"
You will get the whole file as one string, which you can now perform standard functions on.
Outputting "Pattern found" should be trivial then.

Function composition when function parameters in wrong order

I understand that in Haskell it's important to put the most 'variable' argument (aka the input object) last in a function definition, to make the function as composable as possible (reference). But if I'm using a library function that doesn't follow this rule, is there any alternative/workaround to function composition to increase readability?
I'll give a specific example - the subRegex function takes the input string as the first of two arguments, and so chaining two of these plus a toUpper to create a "slugify" function I have ended up with the below:
slugify :: FilePath -> FilePath
slugify old =
let (dir, file) = splitFileName old
in combine dir $ subRegex (mkRegex "[ _]") (subRegex (mkRegex "[^.a-z0-9_ ]+") (map toLower file) "") "-"
Is there a function-composition-style way of tidying this up given the order of the subRegex arguments?
You can always provide a small helper to change the argument order into something more composable:
slugify :: FilePath -> FilePath
slugify old =
let (dir, file) = splitFileName old
in combine dir $ sub "[ _]" "-" $ sub "[^.a-z0-9_ ]+" "" $ map toLower file
where
sub regex replacement input = subRegex (mkRegex regex) input replacement
In simple cases, you can also use flip or a lambda expression, but in this case I think a local definition is nicer. It also makes it easy to remove the duplication of the calls to mkRegex.

Resources