First of all, you cannot (in any obvious way, at least) use Language.Haskell.Interpreter from the tooltip for this. These functions in this module are used to read and run Haskell code, and not for arbitrary structured data.
To read in structured data, you need some kind of parser. Here are some of the options you have:
- Use a parser generator like Happy .
- Use a parser-combinator library such as uu-parsinglib or parsec .
- Directly implement your own parser.
- Use automatically derived instances of the
Read class.
Announcement 1. and 2.
If the data format you need to read is nontrivial, or if you need useful error messages if parsing is not performed, I recommend switching to 1. or 2. and consult the documentation of the corresponding tools and library. Keep in mind that it will take you some time to get used to their basic concepts and interfaces.
Announcement 3.
If the format of your data is simple enough (as in your example), and if extensive reporting of errors is small on your wish list, you can easily collapse your own parser.
In your example, the configuration file is a list of keys and values separated by newlines. In Haskell, we can imagine this enumeration from a list of line pairs:
type Config = [(String, String)]
The “analysis” of the configuration then reduces to: (1) splitting the input line in the lines, (2) splitting each line with words, (3) selecting the first and third words from each line:
readConfig :: String -> Config readConfig s = [(key, val) | line <- lines s, let (key : _ : val : _) = words line]
To get an entry from the analyzed configuration file, we can then use the get function:
get :: String -> (String -> a) -> Config -> a get key f config = case lookup key config of Nothing -> error ("get: not found: " ++ key) Just x -> fx
This function takes the record key as its first argument, and the second argument takes the function that converts the string of the original value to something suitable. For purely textual configuration values, we can simply pass the get identification function:
inputFile, outputFile, datefmt :: Config -> String inputFile = get "inputFile" id outputFile = get "outputFile" id datefmt = get "datefmt" id
For whole entries, we can use Read :
numRecords, numFields :: Config -> Int numRecords = get "numRecords" read numFields = get "numFields" read
Perhaps these patterns are common enough to be considered in their own versions of get :
getS :: String -> Config -> String getS key = get key id getR :: Read a => String -> Config -> a getR key = get key read inputFile', outputFile', datefmt' :: Config -> String inputFile' = getS "inputFile" outputFile' = getS "outputFile" datefmt' = getS "datefmt" numRecords', numFields' :: Config -> Int numRecords' = getR "numRecords" numFields' = getR "numFields"
As an example, here is a program that reads in a configuration file and prints the value for "outputFile":
main :: IO () main = do s <- readFile "config.txt" let config = readConfig s putStrLn (outputFile config)
Announcement 4.
If you can control the format of the configuration file, you can enter a new data type to store the configuration data and Haskell will automatically display an instance of the Read class for it. For instance:
data Config = Config { numRecords :: Int , numFields :: Int , inputFile :: String , outputFile :: String , datefmt :: String } deriving Read
Now you need to make sure your configuration files are in the expected format. For instance:
Config { numRecords = 10 , numFields = 3 , inputFile = "/home/user1/project/indata.file" , outputFile = "/home/user1/project/outdata.file" , datefmt = "ddmmyyyy" }
As an example, here is a program that prints the value for "outputFile":
main :: IO () main = do s <- readFile "config.txt" let config = read s putStrLn (outputFile config)