问题
Sorry for a poor title, feel free to edit. I can't understand what the problem is, so it might be altogether wrong. Below is the code (this is after I've done like a hundred of permutations and different sequences of let-do-if and tabulation, and I'm exhausted):
-- The last statement in a 'do' construct must be an expression
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n"
let intYear = readYear
in if (intYear < 2000 || intYear > 2012)
then error "Year must be withing range: 2000-2012"
else
c <- readIORef connection
[Only i] <- query_ c ("select count('*')" ++
"from table" ++
"where ((acquisition_date <= " ++
(formatDate intYear) ++
") and ((sale_date is null) or " ++
"(sale_date < " ++
(formatDate intYear) ++ ")))")
return i
readYear :: Integer
readYear = do
year <- getLine
read year :: Integer
Something that would meant to be so simple... I still don't understand what is wrong with the code above. Please, if you could kindly explain the source of the error, that would be great. I did read about do, let-in and if-then-else, and I don't see any errors here from what I could understand from the manual.
Ideally, if there are alternatives, I would like very much to reduce the amount of the wasted white space on the left.
Thank you.
回答1:
readYear
is not an Integer
, it's an IO
action that can be run to read input and convert the input to an integer -- in other words, IO Integer
. And as it's an IO
action, you'll need a return
to use whatever read year
as result of getYear
. That is:
getYear :: IO Integer
getYear = do year <- getLine
return (read year)
This also means you use it like intYear <- readYear
instead of using let
(well, you could, but you'd store the IO action instead of running it, and the type of intYear
would be wrong). That is:
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n"
intYear <- readYear
...
do
does not extend over if
, rather you need to start again with do
if you want a sequence of actions in the then
or else
branch. That is:
else
c <- readIORef connection
...
return i
should be roughly:
else do c <- readIORef connection
...
return i
As for reducing whitespace, consider pushing the validation logic into readYear
. Implementing this is left as an exercise to the reader ;)
As an aside, you don't need in
when using let
in a do
block (but only there!), you can simply state:
do do_something
let val = pure_compuation
something_else_using val
回答2:
You need a new do
for every block of monadic functions: simply writing functions in a row has no meaning, regardless of whether they're monadic or pure. And everything where the value comes from the IO
monad must itself give its return value in the monad.
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n" -- why extra '\n'?
intYear <- readYear -- readYear expects user input <- must be monadic
if (intYear < 2000 || intYear > 2012)
then error "Year must be withing range: 2000-2012"
else do
c <- readIORef connection
[Only i] <- query_ c ("select count('*')" ++
"from table" ++
"where ((acquisition_date <= " ++
(formatDate intYear) ++
") and ((sale_date is null) or " ++
"(sale_date < " ++
(formatDate intYear) ++ ")))")
return i
readYear :: IO Integer
readYear = do
year <- getLine
return $ read year :: Integer
Why is an extra
do
needed...
Well, the thing with do
in Haskell is that it's really just syntactic sugar. Let's simplify your function a little
nOG :: IO String
nOG = do putStrLn "Prompt"
someInput <- inputSth
if condition someInput
then error "Bloap"
else do c <- inputSthElse
[only] <- query_ c
return only
what this actually means is
nOG :: IO String
nOG = putStrLn "Prompt"
>> inputSth
>>= (\someInput ->
if condition someInput
then error "Bloap"
else inputSthElse
>>= (\s -> query_ c
>>= (\[only] -> return only )
)
)
Where you should be able to see that if
behaves in exactly the same way as it does in a pure functional expression like shade (r,g,b) = if g>r && g>b then "greenish" else "purpleish"
. It doesn't in any way "know" about all the IO
monad stuff going on around it, so it can't infer that there should again be a do
block in one of its branches.
来源:https://stackoverflow.com/questions/9573388/syntax-confusion-do-block