conditional standard handle redirection in Haskell - haskell

I want to read a file, process it, and write the results to another file; the input file name is to be supplied through a console argument, and the output file name is generated from the input file name.
The catch is I want it to transparently “fail over” to stdin/stdout if no arguments are supplied; essentially, in case a file name is supplied, I redirect stdin/stdout to the respective file names so I can transparently use interact whether the file name was supplied or not.
Here's the code hacked together with dummy output in a superfluous else. What will be the proper, idiomatic form of doing it?
It probably could have something to do with Control.Monad's when or guard, as was pointed out in a similar question, but maybe somebody wrote this already.
import System.IO
import Data.Char(toUpper)
import System.Environment
import GHC.IO.Handle
main :: IO ()
main = do
args <- getArgs
if(not $ null args) then
do
print $ "working with "++ (head args)
finHandle <- openFile (head args) ReadMode --open the supplied input file
hDuplicateTo finHandle stdin --bind stdin to finName's handle
foutHandle <- openFile ((head args) ++ ".out") WriteMode --open the output file for writing
hDuplicateTo foutHandle stdout --bind stdout to the outgoing file
else print "working through stdin/redirect" --get to know
interact ((++) "Here you go---\n" . map toUpper)

There's nothing very special about interact - here is its definition:
interact :: (String -> String) -> IO ()
interact f = do s <- getContents
putStr (f s)
How about something like this:
import System.Environment
import Data.Char
main = do
args <- getArgs
let (reader, writer) =
case args of
[] -> (getContents, putStr)
(path : _) -> let outpath = path ++ ".output"
in (readFile path, writeFile outpath)
contents <- reader
writer (process contents)
process :: String -> String
process = (++) "Here you go---\n" . map toUpper
Based on the command line arguments we set reader and writer to the IO-actions which will read the input and write the output.

This seems fairly idiomatic to me already. The one note I have is to avoid head, as it is an unsafe function (it can throw a runtime error). In this case it is fairly easy to do so by using case to pattern match.
main :: IO ()
main = do
args <- getArgs
case args of
fname:_ -> do
print $ "working with " ++ fname
finHandle <- openFile fname ReadMode
hDuplicateTo finHandle stdin
foutHandle <- openFile (fname ++ ".out") WriteMode
hDuplicateTo foutHandle stdout
[] -> do
print "working through stdin/redirect"
interact ((++) "Here you go---\n" . map toUpper)

Related

haskell readFile from file line by line

Haskell programming
how to use readFile
I use getLine but it should react with a user for each command
But i need to read lines from text file and process the input line
text <- readFile "input.txt"
let linii = lines text
interact (unlines . (map calculate) . linii)
So interact is to do IO using stdin and stdout. If instead of stdin you want to use a file for input, that's fine you can use the readFile function as you are already doing:
applyOnFileLines :: FilePath -> (String -> String) -> IO ()
applyOnFileLines filePath func = do
file <- readFile filePath
putStr . unlines
. map func
. lines
$ file

Haskell Let/In in main function

My code:
import System.IO
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let
someValue = someFunction(content)
in
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
hClose inFile
My error:
- Type error in application
*** Expression : print (anotherFunction2(someValue))
*** Term : print
*** Type : e -> IO ()
*** Does not match : a -> b -> c -> d
I need to print two or more lines with functions that require "someValue".
How I can fix it?
The cause of that error message is that when you write
let
someValue = someFunction(content)
in
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
the two print statements are actually parsed as one:
print (anotherFunction (someValue)) print (anotherFunction2 (someValue))
In other words, it thinks the second print as well as (anotherFunction2 (someValue)) are also arguments to the first print. This is why it complains that e -> IO () (the actual type of print) does not match a -> b -> c -> d (a function taking three arguments).
You can fix this by adding a do after the in to make it parse the two statements as separate:
let
someValue = someFunction(content)
in do
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
Though, it's better to use the do-notation form of let here, without any in:
import System.IO
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let someValue = someFunction content
print (anotherFunction someValue)
print (anotherFunction2 someValue)
hClose inFile
I also got rid of some redundant parentheses in the above code. Remember, they are only used for grouping, not for function application in Haskell.
When you use let binding in a do block, don't use the in keyword.
main :: IO()
main = do
inFile <- openFile "file.txt" ReadMode
content <- hGetContents inFile
let someValue = someFunction(content)
print(anotherFunction(someValue))
print(anotherFunction2(someValue))
hClose inFile

Catching/hijacking stdout in haskell

How can I define 'catchOutput' so that running main outputs only 'bar'?
That is, how can I access both the output stream (stdout) and the actual output of an io action separately?
catchOutput :: IO a -> IO (a,String)
catchOutput = undefined
doSomethingWithOutput :: IO a -> IO ()
doSomethingWithOutput io = do
(_ioOutp, stdOutp) <- catchOutput io
if stdOutp == "foo"
then putStrLn "bar"
else putStrLn "fail!"
main = doSomethingWithOutput (putStr "foo")
The best hypothetical "solution" I've found so far includes diverting stdout, inspired by this, to a file stream and then reading from that file (Besides being super-ugly I haven't been able to read directly after writing from a file. Is it possible to create a "custom buffer stream" that doesn't have to store in a file?). Although that feels 'a bit' like a side track.
Another angle seems to use 'hGetContents stdout' if that is supposed to do what I think it should. But I'm not given permission to read from stdout. Although googling it seems to show that it has been used.
I used the following function for an unit test of a function that prints to stdout.
import GHC.IO.Handle
import System.IO
import System.Directory
catchOutput :: IO () -> IO String
catchOutput f = do
tmpd <- getTemporaryDirectory
(tmpf, tmph) <- openTempFile tmpd "haskell_stdout"
stdout_dup <- hDuplicate stdout
hDuplicateTo tmph stdout
hClose tmph
f
hDuplicateTo stdout_dup stdout
str <- readFile tmpf
removeFile tmpf
return str
I am not sure about the in-memory file approach, but it works okay for a small amount of output with a temporary file.
There are some packages on Hackage that promise to do that : io-capture and silently. silently seems to be maintained and works on Windows too (io-capture only works on Unix). With silently, you use capture :
import System.IO.Silently
main = do
(output, _) <- capture $ putStr "hello"
putStrLn $ output ++ " world"
Note that it works by redirecting output to a temporary file and then read it... But as long as it works !
Why not just use a writer monad instead? For example,
import Control.Monad.Writer
doSomethingWithOutput :: WriterT String IO a -> IO ()
doSomethingWithOutput io = do
(_, res) <- runWriterT io
if res == "foo"
then putStrLn "bar"
else putStrLn "fail!"
main = doSomethingWithOutput (tell "foo")
Alternatively, you could modify your inner action to take a Handle to write to instead of stdout. You can then use something like knob to make an in-memory file handle which you can pass to the inner action, and check its contents afterward.
As #hammar pointed out, you can use a knob to create an in-memory file, but you can also use hDuplicate and hDuplicateTo to change stdout to the memory file, and back again. Something like the following completely untested code:
catchOutput io = do
knob <- newKnob (pack [])
let before = do
h <- newFileHandle knob "<stdout>" WriteMode
stdout' <- hDuplicate stdout
hDuplicateTo h stdout
hClose h
return stdout'
after stdout' = do
hDuplicateTo stdout' stdout
hClose stdout'
a <- bracket_ before after io
bytes <- Data.Knob.getContents knob
return (a, unpack bytes)

cat emulator in Haskell

So I'm attempting to make a program that reads from a handle and writes to stdOut, like so:
import IO
import System
cat w = do
fromHandle <- getAndOpenFile w ReadMode
contents <- hGetContents fromHandle
putStr contents
putStr "Done."
getAndOpenFile :: String -> IOMode -> IO Handle
getAndOpenFile name mode =
do
catch (openFile name mode)
(\_ -> do
putStrLn ("Cannot open "++ name ++"\n")
return())
I'm fairly new to Hs and it seems like this should be far more simple than I'm making it for myself. Any suggestions to helping me move further?
the usage would be ./cat "foo.txt" and would print the text in the foo.txt file to stdOut.
There is the below function which does what you want.
readFile :: FilePath -> IO String
use this with putStr to print the IO String

Idiomatic way to conditionally process IO in Haskell

I'm writing a little shell script in Haskell which can take an optional argument. However, if the argument is not present, I'd like to get a line from stdin in which to ask for a value.
What would be the idiomatic way to do this in Haskell?
#!/usr/bin/env runhaskell
import Control.Applicative ((<$>))
import Data.Char (toLower)
import IO (hFlush, stdout)
import System.Environment (getArgs)
main :: IO ()
main = do args <- getArgs
-- here should be some sort of branching logic that reads
-- the prompt unless `length args == 1`
name <- lowerCase <$> readPrompt "Gimme arg: "
putStrLn name
lowerCase = map toLower
flushString :: String -> IO ()
flushString s = putStr s >> hFlush stdout
readPrompt :: String -> IO String
readPrompt prompt = flushString prompt >> getLine
Oh, and if there's a way to do it with something from Control.Applicative or Control.Arrow I'd like to know. I've become quite keen on these two modules.
Thanks!
main :: IO ()
main = do args <- getArgs
name <- lowerCase <$> case args of
[arg] -> return arg
_ -> readPrompt "Gimme arg: "
putStrLn name
This doesn't fit your specific use case, but the question title made me think immediately of when from Control.Monad. Straight from the docs:
when :: Monad m => Bool -> m () -> m ()
Conditional execution of monadic expressions.
Example:
main = do args <- getArgs
-- arg <- something like what FUZxxl did..
when (length args == 1) (putStrLn $ "Using command line arg: " ++ arg)
-- continue using arg...
You can also use when's cousin unless in similar fashion.

Resources