Alright so here's my current code:
import System.IO
import System.Environment
import System.Directory
main = do
unfiltered <- getArgs ; home <- getHomeDirectory ; let db = home ++ "/.grindstone"
case unfiltered of
(x:xs) -> return ()
_ -> error "No command given. See --help for more info."
command:args <- getArgs
createDirectoryIfMissing True db
let check = case args of
[] -> error "No arguments given. See --help for more info."
_ -> do let (params#(param:_),rest) = span (\(c:_) -> c=='-') args
if length params > 1 then error ("No arguments given for " ++ param)
else do
let (pArgs,_) = span (\(c:_) -> c/='-') rest
return (param, pArgs) :: Either (IO ()) (String, [String])
let add = print "sup"
let cmds = [("add", add)]
let action = lookup command cmds
case action of
Nothing -> error "Unknown command."
(Just action) -> action
The main problem is with check. I tried implementing the Either type since I want it to either error out, or return something for another function to use, but, it's currently erroring out with:
grindstone.hs:21:23:
No instance for (Monad (Either (IO ())))
arising from a use of `return' at grindstone.hs:21:23-43
Possible fix:
add an instance declaration for (Monad (Either (IO ())))
In the expression:
return (param, pArgs) :: Either (IO ()) (String, [String])
In the expression:
do { let (pArgs, _) = span (\ (c : _) -> ...) rest;
return (param, pArgs) :: Either (IO ()) (String, [String]) }
In the expression:
if length params > 1 then
error ("No arguments given for " ++ param)
else
do { let (pArgs, _) = ...;
return (param, pArgs) :: Either (IO ()) (String, [String]) }
I'm only starting out in haskell and haven't dealt too much with monads yet so just thought I'd ask on here. anyone have any ideas?
The error that is causing your compile problems is that you are directly casting an expression to the type Either (IO ()) (String, [String]) when it is not an Either value. (The compiler is not outputting a very helpful error message.)
To create an Either value [1], we use the data constructors Left and Right. Convention (from the library page) is that errors are a Left value, while correct values are a Right value.
I did a quick rewrite of your arg checking function as
checkArgs :: [String] -> Either String (String, [String])
checkArgs args =
case args of
[] -> Left "No arguments given. See --help for more info."
_ -> let (params#(param:_),rest) = span (\(c:_) -> c=='-') args in
if length params > 1 then
Left ("No arguments given for " ++ param)
else
let (pArgs,_) = span (\(c:_) -> c/='-') rest in
Right (param, pArgs)
Note that the arg checking function does not interact with any external IO () library functions and so has a purely functional type. In general if your code does not have monadic elements (IO ()), it can be clearer to write it in purely functional style. (When starting out in Haskell this is definitely something I would recommend rather than trying to get your head around monads/monad transformers/etc immediately.)
When you are a little more comfortable with monads, you may want to check out Control.Monad.Error [2], which can wraps similar functionality as Either as a monad and would encapsulate some details like Left always being computation errors.
[1] http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Either.html
[2] http://hackage.haskell.org/packages/archive/mtl/1.1.0.2/doc/html/Control-Monad-Error.html
Either (IO ()) (String, [String]) is a type that contains an IO action or a
(String, [String]), so values of this type could be Left IO () or
Right (String, [String]). Left values usually represents an error
occurrence in Haskell. This error can be represented with any type you want,
for example, an error code (Int) or a String that says what happened.
If you use IO () as the type which represents an error, you won't be able
to extract any information about the error. You just will able to perform an IO action later on.
The type that you are looking for isn't Either (IO ()) (String, [String]),
is Either String (String, [String]). With this type can get information about the
error (String). Now, you dont need any IO action into Either type, so you
can remove all do expressions:
let check = case args of
[] -> Left "No arguments given. See --help for more info."
_ -> let (params#(param:_),rest) = span (\(c:_) -> c=='-') args
in if length params > 1
then Left ("No arguments given for " ++ param)
else let (pArgs,_) = span (\(c:_) -> c/='-') rest
in Right (param, pArgs)
Related
In the main function of my program, I call timesRule which is returning a Boolean value. In this function I want to write to a file. However, if I understood correctly the function times rule needs to return IO() if it writes to file.
How should I structure my code to write to a file in a function returning a Boolean value ?
timesRule :: (MultiSet LocalType) -> Bool
timesRule sequent = do
let result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent
let file = "tmp/log.txt"
let content = "TIMES rule: " ++ (show(MultiSet.toList result))
let log = writeToFile file content
prefixRule result
Used function:
import qualified System.IO.Strict as SIO
writeToFile :: FilePath -> String -> IO()
writeToFile file content = do
x <- SIO.readFile file
writeFile file ("\n"++content)
appendFile file x
The somewhat obvious solution would be changing the type your function to IO Bool as #Robin Zigmond pointed out.
There is some problem with your syntax apart from calling writeToFile, though. For your function timesRule to have the given type it would need to look like this:
timesRule :: (MultiSet LocalType) -> Bool
timesRule sequent = -- there is no do here
let
result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent
-- the following two lines are superfluous ...
file = "tmp/log.txt"
content = "TIMES rule: " ++ (show(MultiSet.toList result))
-- ... because this still doesn't work
log = writeToFile file content
-- ... and what were you going to use `log` for, anyway?
in
prefixRule result
Changing your type to IO Bool allows you to use monadic do-syntax. Bool by itself neither has an applicative nor a monad instance and thus there is no meaningful do-syntax. (In order to have either an applicative or a monad instance, you need a type function like Maybe or IO, fyi):
timesRule :: (MultiSet LocalType) -> IO Bool
timesRule sequent = do
let
result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent
file = "tmp/log.txt"
content = "TIMES rule: " ++ (show(MultiSet.toList result))
-- the syntax to get the value of type `a` out of `IO a` is this:
log <- writeToFile file content
-- the function to turn a value of type `a` into `IO a` is `pure`:
pure (prefixRule result)
You stil don't use log and might as well replace
log <- writeToFile file content
with
writeToFile file content
Given that writeToFile has type ... -> IO (), the () is pronounced "unit", the value of log is () and thus log does not contain any useful information (probably).
The less obvious solution is to refactor your code a bit and seperate the concerns. Sometimes it does make sense to have a function write to a file and return some boolean value. In your case, you probably want a funcion that returns result, i.e. turn this line into a function:
MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent
Then you already have prefixRule that gives you the Bool and you have writeFile. This way you separate pure code (anything that does not have the type IO something) form code with IO-side effects.
I have a function "management" that checks parameters and return a Maybe (String):
If there are not parameter -> return Nothing
If my parameter is equal to "-h" -> Return a string help
My problem arrived when I get a file and check if this file exists.
Couldn't match expected type ‘Maybe String’
with actual type ‘IO (Either e0 a2)’
managagment :: [String] -> Maybe (String)
managagment [] = Nothing
managagment ["-h"] = Just (help)
managagment [file] = try $ readFile file >>= \result -> case result of
Left e -> Nothing
Right content -> Just (content)
There are several problems
Function application ($) is lower precedence than bind (>>=)
You said:
try $ readFile file >>= \res...
Which means
try ( readFile file >>= \res... )
But you wanted:
try ( readFile file ) >>= \res...
IO (Maybe a) and Maybe a are distinct
You have a function using IO (via readFile and try) but many of the cases do not return an IO result (Nothing and Just content).
Solution: Return via return Nothing or pure Nothing to lift a value into the IO monad.
The exception type was ambiguous
The try function can catch any exception type, just look at the signature:
try :: Exception e => IO a -> IO (Either e a)
When you totally ignore the exception you leave the type checker with no information to decide what e should be. In these situations an explicit type signature is useful, such as:
Left (e::SomeException) -> pure Nothing
managagment is partial
managagment ["a","b"] is undefined as is any input list of length over one. Consider a final equational definition of:
managagment _ = managagment ["-h"]
Or more directly:
managagment _ = pure $ Just help
Style and other notes
managagment should probably management
Just (foo) is generally Just foo
help is not a function that returns a String. It is a value of type String.
The example was not complete, missing imports and help.
Fixed Code
Consider instead:
#!/usr/bin/env cabal
{- cabal:
build-depends: base
-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
import Control.Exception (try, SomeException)
main :: IO ()
main = print =<< management []
help :: String
help = "so helpful"
management :: [String] -> IO (Maybe String)
management [] = pure Nothing
management ["-h"] = pure $ Just help
management [file] =
try (readFile file) >>=
\case
Left (e::SomeException) -> pure Nothing
Right content -> pure $ Just content
management _ = pure $ Just help
And test as such:
% chmod +x x.hs
% ./x.hs
I am trying following code with try-catch block:
import System.Environment
import System.IO
import System.IO.Error
import Control.Exception
isBinary :: String -> Bool
isBinary ss = do
print "In isBinary fn" -- works if this line is removed.
let ans = any (\c -> ord c > 127) ss
ans
toTry :: String -> IO ()
toTry firline = do
print "In toTry fn."
let answer = isBinary firline
if not answer then do
print "Sent line not binary: "
else
print "Sent line binary"
handler :: IOError -> IO ()
handler e = putStrLn "Whoops, had some trouble!"
ss = "this is a test"
main = do
toTry ss `catch` handler
However, I am getting following error:
$ runghc trycatch3.hs
trycatch3.hs:9:9: error:
• Couldn't match expected type ‘Bool’ with actual type ‘IO Bool’
• In a stmt of a 'do' block: print "in isBinary fn"
In the expression:
do { print "in isBinary fn";
let ans = any (\ c -> ...) ss;
return ans }
In an equation for ‘isBinary’:
isBinary ss
= do { print "in isBinary fn";
let ans = ...;
return ans }
trycatch3.hs:10:30: error:
• Variable not in scope: ord :: Char -> Integer
• Perhaps you meant one of these:
‘or’ (imported from Prelude), ‘odd’ (imported from Prelude)
The error goes away and program works well if the print statement is removed from isBinary function.
Why can't I put print statement in this function?
The answer is, "because types". Specifically:
isBinary :: String -> Bool
isBinary ss = do
....
Since it's a do block, the return type of isBinary must match a monadic type Monad m => m t for some m and some t. Here, since print "" :: IO (), m is IO, so it should've been
isBinary :: String -> IO Bool
isBinary ss = do
and now
print "In isBinary fn" -- works
let ans = any (\c -> ord c > 127) ss -- also works
ans -- doesn't work
ans doesn't work because of types, again. Its type is Bool, but it must be IO Bool -- first, because this do block belongs to IO monad, on account of print; and second, because of the return type of the function as a whole.
Instead, use
return ans
and now it'll work, because return injects a value into the monadic context, and being the last do block value it becomes the value produced by the do block overall (if return val appears in the middle it just passes the val to the next step in the combined computation).
The function toTry will have to be augmented to use the new definition:
toTry :: String -> IO ()
toTry firline = do
print "In toTry fn."
-- let answer = isBinary firline -- incorrect, now!
answer <- isBinary firline -- isBinary ... :: IO Bool
if not answer then do -- answer :: Bool
print "Sent line not binary: "
else
print "Sent line binary"
m a on the right of <-, a on the left.
See this for a general description of do notation.
You might be confused by the same print line working in toTry, but not in isBinary. The difference stems from the declaration:
isBinary :: String -> Bool
This means that isBinary is a pure function (i.e. no side effects), taking a string and returning a boolean. In fact, you could simplify it to
isBinary ss = any (\c -> ord c > 127) ss
or even use the point-free style
isBinary = any (\c -> ord c > 127)
However, toTry is
toTry :: String -> IO ()
I.e. it takes a string and returns the IO monad which is impure (can have side effects, such as printing text to the console).
Haskell is a language that encourages using pure functions, and enforces it using the type system, by forcing the programmer to explicitly mark impure code.
Further reading:
What does "pure" mean in "pure functional language"?
Looking at your code, it seems your use of print in isBinary is not an integral part of what you want the function to do, but merely a debug print statement that will be removed later on. In that case, you do not want to change the type of isBinary to String -> IO Bool (for more on that, see Will Ness' answer), as you don't actually need IO except for debugging. Rather, the core libraries offer the Debug.Trace module, which caters to this kind of situation. With it, we can add your debug print statement like this:
isBinary :: String -> Bool
isBinary ss = trace "In isBinary fn" $ any (\c -> ord c > 127) ss
Then, once you are done debugging, you can remove the use of trace -- and it bears repeating you really should do that later on. Quoting the Debug.Trace documentation:
Functions for tracing and monitoring execution.
These can be useful for investigating bugs or performance problems. They should not be used in production code.
I am still quite new to Haskell so forgive me if this is completely obvious and I am just not understanding correctly.
On Hackage the documentation says that System.Console.GetOpt ReqArg takes a function of arity 1 e.g (String -> a) as the first argument to its constructor.
ReqArg (String -> a) String
In many of the examples that I have seen a 2 arity function is passed to this constructor.
Example from (https://wiki.haskell.org/High-level_option_handling_with_GetOpt):
data Options = Options { optVerbose :: Bool
, optInput :: IO String
, optOutput :: String -> IO ()
}
startOptions :: Options
startOptions = Options { optVerbose = False
, optInput = getContents
, optOutput = putStr
}
options :: [ OptDescr (Options -> IO Options) ]
options =
[ Option "i" ["input"]
(ReqArg
(\arg opt -> return opt { optInput = readFile arg })
"FILE")
"Input file"
, Option "o" ["output"]
(ReqArg
(\arg opt -> return opt { optOutput = writeFile arg })
"FILE")
"Output file"
, Option "s" ["string"]
(ReqArg
(\arg opt -> return opt { optInput = return arg })
"FILE")
"Input string"
, Option "v" ["verbose"]
(NoArg
(\opt -> return opt { optVerbose = True }))
"Enable verbose messages"
, Option "V" ["version"]
(NoArg
(\_ -> do
hPutStrLn stderr "Version 0.01"
exitWith ExitSuccess))
"Print version"
, Option "h" ["help"]
(NoArg
(\_ -> do
prg <- getProgName
hPutStrLn stderr (usageInfo prg options)
exitWith ExitSuccess))
"Show help"
]
So my question is do value constructors not really enforce the type when a function is used in its arguments or is there something else I am missing?
Update:
This is making more sense to me know. I believe there were a couple of factors that I was overlooking. First, as #CommuSoft mentioned, all functions really are a single arity in Haskell due to currying. Second, I didn't look closely enough at options which is not a function but a variable which is of type:
[ OptDescr (Options -> IO Options) ]
This type signature of options declares what the type of the type variable of ReqArg is as well as the other type constructors NoArg and OptArg (the latter not utilized in the example).
The single arity anonymous function passed to the NoArg ArgDescr constructor will essentially just be:
(Options -> IO Options)
E.g it will receive the Options instance record
Where as the 2 arity anonymous function passed to the ReqArg constructor will be:
(String -> Options -> IO Options)
And it will receive a string (the value someone entered at the command line) and the Options instance record.
Thanks to all for helping me think this through!
The -> you see in type signatures is, actually, a type too. And because of this, type variable a can be a function b -> c. In your example it is Options -> IO Options.
ReqArg is not a function: it is a constructor. Now constructors are evidently functions as well. The signature of ReqArg is:
ReqArg :: (String -> a) -> String -> ArgDescr a
So you constructor returns an ArgDescr a.
Now a second aspect you have to notice is that a is in this case equivalent to a = Options -> IO Options, so that means the signature of your ReqArg constructor collapses to:
ReqArg :: (String -> (Options -> IO Options)) -> String -> ArgDescr (Options -> IO Options)
or less noisy:
ReqArg :: (String -> Options -> IO Options) -> String -> ArgDescr (Options -> IO Options)
(brackets removed)
So it is a function with "arity" 2 (note that strictly speaking in functional programming every function has (at least conceptually) arity 1). The point is that you generate out of the first argument a new function. But Haskells syntactical sugar allows to "define two arguments" at once.
Explaining the documentation
That's the reason why in the documentation example, you need to use foldr:
return (foldl (flip id) defaultOptions o, n)
Note this does not map on your (Options -> IO Options), in the example one uses Options -> Options.
The point is, in the documentation Haskell processes each command option individually. Initially you start with defaultOptions, and processing an option with o out of n results in a new Option, that you use as input for processing the next. After you completed the chain of elements, you return the final Options data.
For your program
You make things a bit harder using an IO Monad: it was perhaps better to store a boolean whether you had to print the version, and if that was the case, do this in the main, or somewhere else. Nevertheless, you can achieve the same using foldlM instead of foldl.
Something that happens to me a lot while web programming: I want to run an operation that has a chance of failure. On a failure, I want to send the client a 500. Normally though, I just want to continue executing a series of steps.
doSomeWebStuff :: SomeWebMonad ()
doSomeWebStuff = do
res <- databaseCall
case res of
Left err -> status 500
Right val -> do
res2 <- anotherDatabaseCall (someprop val)
case res2 of
Left err -> status 500
Right val2 -> text $ show val2
since the errs are exceptions, I don't like that I need all that case stuff just to catch them. I want to do the same thing whenever anything is a left. Is there a way to express that on one line with something like guard, but control what it returns on an exit?
In another language I could do this:
function doSomeWebStuff() {
var res = databaseCall()
if (res == Error) return status 500
var res2 = anotherDatabaseCall(res.someprop)
if (res2 == Error) return status 500
return text(res2)
}
So, I'm ok writing some boilerplate, but I don't want the errors to mess with my nesting, when it's far more common to just want to continue forward with the found case.
What's the cleanest way to do this? I know in theory I can use a monad to exit early on a failure, but I've only seen examples with Maybe and it would return Nothing at the end, rather than letting me specify what it returns.
Here's how I would do it with ErrorT. Disclaimer: I have never actually used ErrorT before.
webStuffOr500 :: ErrorT String SomeWebMonad () -> SomeWebMonad ()
webStuffOr500 action = do
res <- runErrorT action
case res of
Left err -> do
logError err -- you probably want to know what went wrong
status 500
Right () -> return ()
doSomeWebStuff :: SomeWebMonad ()
doSomeWebStuff = webStuffOr500 doSomeWebStuff'
doSomeWebStuff' :: ErrorT String SomeWebMonad ()
doSomeWebStuff' = do
val <- ErrorT databaseCall
val2 <- ErrorT $ anotherDatabaseCall (someprop val)
lift $ text $ show val2
Here are the imports and type declarations I used to make sure it all typechecks correctly:
import Control.Monad.Identity
import Control.Monad.Error
import Control.Monad.Trans (lift)
import Control.Monad
type SomeWebMonad = Identity
data Foo = Foo
data Bar = Bar
data Baz = Baz deriving (Show)
someprop :: Foo -> Bar
someprop = undefined
databaseCall :: SomeWebMonad (Either String Foo)
databaseCall = undefined
anotherDatabaseCall :: Bar -> SomeWebMonad (Either String Baz)
anotherDatabaseCall = undefined
logError :: String -> SomeWebMonad ()
logError = undefined
text :: String -> SomeWebMonad ()
text = undefined
status :: Int -> SomeWebMonad ()
status = undefined
If I'm doing this all wrong then please, somebody shout out. It may be wise, if you take this approach, to modify the type signature of databaseCall and anotherDatabaseCall to also use ErrorT, that way a <- ErrorT b can be reduced to a <- b in doSomeWebStuff'.
Since I'm a complete noob at ErrorT, I can't really do any hand-holding besides "here's some code, go have some fun".
Not a direct answer to your question, but have you considered using Snap? In snap, we have short-circuiting behavior built-in with an idiomatic:
getResponse >>= finishWith
where
finishWith :: MonadSnap m => Response -> m a
So given a response object, it will terminate early (and match whatever type comes after that). Haskell laziness will ensure computations within Snap monad after finishWith won't be executed.
I sometimes make a little helper:
finishEarly code str = do
modifyResponse $ setResponseStatus code str
modifyResponse $ addHeader "Content-Type" "text/plain"
writeBS str
getResponse >>= finishWith
which I can then use anywhere in my handlers.
myHandler = do
x <- doSomething
when (x == blah) $ finishEarly 400 "That doesn't work!!"
doOtherStuff