Idiomatic way to conditionally process IO in Haskell - 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.

Related

How to correctly parse arguments with Haskell?

I'm trying to learn how to work with IO in Haskell by writing a function that, if there is a flag, will take a list of points from a file, and if there is no flag, it asks the user to enter them.
dispatch :: [String] -> IO ()
dispatch argList = do
if "file" `elem` argList
then do
let (path : otherArgs) = argList
points <- getPointsFile path
else
print "Enter a point in the format: x;y"
input <- getLine
if (input == "exit")
then do
print "The user inputted list:"
print $ reverse xs
else (inputStrings (input:xs))
if "help" `elem` argList
then help
else return ()
dispatch [] = return ()
dispatch _ = error "Error: invalid args"
getPointsFile :: String -> IO ([(Double, Double)])
getPointsFile path = do
handle <- openFile path ReadMode
contents <- hGetContents handle
let points_str = lines contents
let points = foldl (\l d -> l ++ [tuplify2 $ splitOn ";" d]) [] points_str
hClose handle
return points
I get this: do-notation in pattern Possibly caused by a missing 'do'?` after `if "file" `elem` argList.
I'm also worried about the binding issue, assuming that I have another flag that says which method will be used to process the points. Obviously it waits for points, but I don't know how to make points visible not only in if then else, constructs. In imperative languages I would write something like:
init points
if ... { points = a}
else points = b
some actions with points
How I can do something similar in Haskell?
Here's a fairly minimal example that I've done half a dozen times when I'm writing something quick and dirty, don't have a complicated argument structure, and so can't be bothered to do a proper job of setting up one of the usual command-line parsing libraries. It doesn't explain what went wrong with your approach -- there's an existing good answer there -- it's just an attempt to show what this kind of thing looks like when done idiomatically.
import System.Environment
import System.Exit
import System.IO
main :: IO ()
main = do
args <- getArgs
pts <- case args of
["--help"] -> usage stdout ExitSuccess
["--file", f] -> getPointsFile f
[] -> getPointsNoFile
_ -> usage stderr (ExitFailure 1)
print (frobnicate pts)
usage :: Handle -> ExitCode -> IO a
usage h c = do
nm <- getProgName
hPutStrLn h $ "Usage: " ++ nm ++ " [--file FILE]"
hPutStrLn h $ "Frobnicate the points in FILE, or from stdin if no file is supplied."
exitWith c
getPointsFile :: FilePath -> IO [(Double, Double)]
getPointsFile = {- ... -}
getPointsNoFile :: IO [(Double, Double)]
getPointsNoFile = {- ... -}
frobnicate :: [(Double, Double)] -> Double
frobnicate = {- ... -}
if in Haskell doesn't inherently have anything to do with control flow, it just switches between expressions. Which, in Haskell, happen to include do blocks of statements (if we want to call them that), but you still always need to make that explicit, i.e. you need to say both then do and else do if there are multiple statements in each branch.
Also, all the statements in a do block need to be indented to the same level. So in your case
if "file" `elem` argList
...
if "help" `elem` argList
Or alternatively, if the help check should only happen in the else branch, it needs to be indented to the statements in that do block.
Independent of all that, I would recommend to avoid parsing anything in an IO context. It is usually much less hassle and easier testable to first parse the strings into a pure data structure, which can then easily be processed by the part of the code that does IO. There are libraries like cmdargs and optparse-applicative that help with the parsing part.

Idiomatic way to share variables between functions in Haskell?

I have a situation where a recursive function makes a decision based on the command line arguments. The recursive function is not called directly by main. I'm wondering what the best way is to make the arguments available to the function. I do not want to call getArgs inside the recursive function, because that seems like it would add a lot of overhead.
However, it is awkward to call getArgs in main and then pass the arguments through a function that doesn't use them. This example is not recursive, but hopefully you get the concept.
import Data.Char
import System.Environment
main :: IO ()
main = do
args <- getArgs -- want to use these args in fun2
traverse_ fun1 ["one", "two", "three"]
fun1 :: String -> IO ()
fun1 s = traverse_ fun2 s
fun2 :: Char -> IO ()
fun2 c = do
if "-u" `elem` args then print $ toUpper c -- how to get args here?
else print $ toLower c
Passing the arguments around seems like a bad idea:
import Data.Char
import System.Environment
main :: IO ()
main = do
args <- getArgs -- want to use these args in fun2
traverse_ (fun1 args) ["one", "two", "three"]
fun1 :: [String] -> String -> IO ()
fun1 args s = traverse_ (fun2 args) s
fun2 :: [String] -> Char -> IO ()
fun2 args c = do
if "-u" `elem` args then print $ toUpper c
else print $ toLower c
In an object-oriented language, you would just have a member variable in a class, or some sort of global variable.
There is nothing awkward about passing arguments to fun1 - it does use them (passing them to func2 is using them).
What is awkward, is to have your fun1 or fun2's behavior depend on hidden variables, making their behaviors difficult to reason about or predict.
Another thing you can do: make fun2 an argument to fun1 (you can pass functions as parameters in Haskell!):
fun1 :: (Char -> IO ()) -> String -> IO ()
fun1 f s = traverse_ f s
Then, you can call it in main like this:
traverse_ (fun1 (fun2 args)) ["one", "two", "three"]
That way you can pass the arguments directly to fun2, then pass fun2 to fun1...
For cases when you really do need a shared, read-only environment, use the Reader monad, or in this case, the ReaderT monad transformer.
import Data.Char
import Data.Foldable
import System.Environment
import Control.Monad.Trans
import Control.Monad.Trans.Reader
main :: IO ()
main = do
args <- getArgs
-- Pass in the arguments using runReaderT
runReaderT (traverse_ fun1 ["one", "two", "three"]) args
-- The type changes, but the body stays the same.
-- fun1 doesn't care about the environment, and fun2
-- is still a Kleisli arrow; traverse_ doesn't care if
-- its type is Char -> IO () or Char -> ReaderT [String] IO ()
fun1 :: String -> ReaderT [String] IO ()
fun1 s = traverse_ fun2 s
-- Get the arguments using ask, and use liftIO
-- to lift the IO () value produced by print
-- into monad created by ReaderT
fun2 :: Char -> ReaderT [String] IO ()
fun2 c = do
args <- ask
liftIO $ if "-u" `elem` args
then print $ toUpper c
else print $ toLower c
As an aside, you can refactor fun2 slightly:
fun2 :: Char -> ReaderT [String] IO ()
fun2 c = do
args <- ask
let f = if "-u" `elem` args then toUpper else toLower
liftIO $ print (f c)
In fact, you can select toUpper or toLower as soon as you get the arguments, and put that, rather than the arguments themselves, in the environment.
main :: IO ()
main = do
args <- getArgs
-- Pass in the arguments using runReaderT
runReaderT
(traverse_ fun1 ["one", "two", "three"])
(if "-u" `elem` args then toUpper else toLower)
fun1 :: String -> ReaderT (Char -> Char) IO ()
fun1 s = traverse_ fun2 s
fun2 :: Char -> ReaderT (Char -> Char) IO ()
fun2 c = do
f <- ask
liftIO $ print (f c)
The environment type can be any value. The above examples show a list of strings and a single Char -> Char as the environment. In general, you might want a custom product type that holds whatever values you want to share with the rest of your code, for example,
data MyAppConfig = MyAppConfig { foo :: Int
, bar :: Char -> Char
, baz :: [Strings]
}
main :: IO ()
main = do
args <- getArgs
-- Process arguments and define a value of type MyAppConfig
runReaderT fun1 MyAppConfig
fun1 :: ReaderT MyAppConfig IO ()
fun1 = do
(MyAppConfig x y z) <- ask -- Get the entire environment and unpack it
x' <- asks foo -- Ask for a specific piece of the environment
...
You may want to read more about the ReaderT design pattern.
While the answer by typedfern is good (upvoted), it'd be even more idiomatic to write as many pure functions as possible, and then defer the effects until the time where you can no longer postpone them. This enables you to create a pipeline of data instead of having to pass arguments around.
I understand that the example problem shown in the OP is simplified, possibly to the degree that it's trivial, but it'd be easier to compose if you separate the logic from its effects.
First, rewrite fun2 to a pure function:
fun2 :: Foldable t => t String -> Char -> Char
fun2 args c =
if "-u" `elem` args then toUpper c else toLower c
If you partially apply fun2 with your arguments, you have a function with the type Char -> Char. The data (["one", "two", "three"]) you wish to print, however, has the type [[Char]]. You want to apply each of the Char values to fun2 args. That's essentially what the OP fun1 function does.
You can, however, instead flatten the [[Char]] value to [Char] with join (or concat).
*Q56438055> join ["one", "two", "three"]
"onetwothree"
Now you can simply apply each of the Char values in the flattened list to fun2 args:
*Q56438055> args = ["-u"]
*Q56438055> fmap (fun2 args) $ join ["one", "two", "three"]
"ONETWOTHREE"
This is still a pure result, but you can now apply the effect by printing each of the characters:
main :: IO ()
main = do
args <- getArgs
mapM_ print $ fmap (fun2 args) $ join ["one", "two", "three"]
By changing the function design so that you're passing data from function to function, you can often simplify the code.

type of main function doesn't mean to my application

As we know main function has type IO ().
However, it is problem for me, because my program may return error. It means that I am executing from main function something like that:
ErrorT String IO ()
Of course, at this moment I have problem with type errors.
What should I do ?
args <- getArgs
s <- readFile $ head args
myFoo s
Where myFoo :: String -> ErrorT String IO ()
You need to run it with runErrorT:
runErrorT :: ErrorT e m a -> m (Either e a)
Since myFoo returns a ErrorT String IO () this will evaluate to an IO (Either String ()) which you execute in main and match on the result:
args <- getArgs
s <- readFile $ head args
result <- runErrorT (myFoo s)
case result of
Right _ -> putStrLn "ok!"
Left(err) -> putStrLn $ "Error: " ++ err
To expand on #Lee's answer, you can then use exitFailure and exitSuccess from System.Exit to return an appropriate error code to the calling process:
module Main (main) where
import Control.Monad.Error
import System.Environment
import System.Exit
myFoo :: String -> ErrorT String IO ()
myFoo = undefined
main :: IO ()
main = do
args <- getArgs
s <- readFile $ head args
result <- runErrorT (myFoo s)
case result of
Right _ -> do
putStrLn "OK"
exitSuccess
Left (e) -> do
putStrLn $ "Error: " ++ e
exitFailure

How do I break out of a loop in Haskell?

The current version of the Pipes tutorial, uses the following two functions in one of the example:
stdout :: () -> Consumer String IO r
stdout () = forever $ do
str <- request ()
lift $ putStrLn str
stdin :: () -> Producer String IO ()
stdin () = loop
where
loop = do
eof <- lift $ IO.hIsEOF IO.stdin
unless eof $ do
str <- lift getLine
respond str
loop
As is mentinoed in the tutorial itself, P.stdin is a bit more complicated due to the need to check for the end of input.
Are there any nice ways to rewrite P.stdin to not need a manual tail recursive loop and use higher order control flow combinators like P.stdout does? In an imperative language I would use a structured while loop or a break statement to do the same thing:
while(not IO.isEOF(IO.stdin) ){
str <- getLine()
respond(str)
}
forever(){
if(IO.isEOF(IO.stdin) ){ break }
str <- getLine()
respond(str)
}
I prefer the following:
import Control.Monad
import Control.Monad.Trans.Either
loop :: (Monad m) => EitherT e m a -> m e
loop = liftM (either id id) . runEitherT . forever
-- I'd prefer 'break', but that's in the Prelude
quit :: (Monad m) => e -> EitherT e m r
quit = left
You use it like this:
import Pipes
import qualified System.IO as IO
stdin :: () -> Producer String IO ()
stdin () = loop $ do
eof <- lift $ lift $ IO.hIsEOF IO.stdin
if eof
then quit ()
else do
str <- lift $ lift getLine
lift $ respond str
See this blog post where I explain this technique.
The only reason I don't use that in the tutorial is that I consider it less beginner-friendly.
Looks like a job for whileM_:
stdin () = whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) (lift getLine >>= respond)
or, using do-notation similarly to the original example:
stdin () =
whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) $ do
str <- lift getLine
respond str
The monad-loops package offers also whileM which returns a list of intermediate results instead of ignoring the results of the repeated action, and other useful combinators.
Since there is no implicit flow there is no such thing like "break". Moreover your sample already is small block which will be used in more complicated code.
If you want to stop "producing strings" it should be supported by your abstraction. I.e. some "managment" of "pipes" using special monad in Consumer and/or other monads that related with this one.
You can simply import System.Exit, and use exitWith ExitSuccess
Eg. if (input == 'q')
then exitWith ExitSuccess
else print 5 (anything)

Haskell calculator

Here is my new main with the error: parse error on input '->'
I commented where the error is. Could it be an indentation error somewhere?
main :: IO()
main = do
expression <- evaluate_input
putStrLn $ show $ compute expression
evaluate_input :: IO ()
evaluate_input = do
args <- getArgs
case args of
a:s -> return a
-> do putStrLn "Enter Expression or 'end' to exit calculator"
hFlush stdout
getLine
unless (expression -> "end") $ showExpr expression --error here
where
showExpr expression = do putStrLn $ evaluateExpr expression
evaluate_input
evaluateExpr :: String -> String
evaluateExpr = show
Few problems with your code
until is not used correctly. I find it better to recurse when I have to repeat same action again and again. You can write the monadic version of until and use that.
It is better to use getArgs inside main once. You don't need to repeat it every time.
A corrected version is here. I haven't implemented all the functions so you still need to do the hard work of parsing and evaluating expressions.
import Control.Monad (unless)
main :: IO ()
main = evaluate
evaluate :: IO ()
evaluate = do
putStrLn "Enter Expression"
expr <- getLine
unless (expr == "end") $ showExpr expr
where
showExpr expr = do putStrLn $ evaluateExpr expr
evaluate
evaluateExpr :: String -> String
evaluateExpr = show

Resources