问题
I /think/ I have a similar misunderstanding of the language in two places involving how variable assignment works in do blocks, involving the IO monad. Could you help me understand (1) is it the same misunderstanding, (2) how to clear it up (in an answer, and maybe specifically if you have a favorite reference on the subject)?
I find that I can perform an operation successfully when it is all one line, but not when I try to split into 2 for readability.
Part I: Turning 1 line into 2
Why does this work?
ipg :: IO ()
ipg = do
conn <- connect defaultConnectInfo { connectHost = "0.0.0.0"}
res <- execute conn "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just"Test")
print res
But this not work
ipg :: IO ()
ipg = do
conn <- connect defaultConnectInfo { connectHost = "0.0.0.0" }
q <- "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just"Test")
res <- execute conn q
print res
Gives me:
Couldn't match expected type ‘IO a0’
with actual type ‘q0 -> IO GHC.Int.Int64’
Probable cause: ‘execute’ is applied to too few arguments
In a stmt of a 'do' block: res <- execute conn q
The difference between the first and second being trying to store the query portion in q.
Part II: Turning 2 lines into 1
Why does this work:
myinput :: IO ()
myinput = do
putStrLn "Please input a number."
mynum :: Int <- readLn
print mynum
But this not work?
myinput :: IO ()
myinput = do
mynum :: Int <- readLn $ putStrLn "Please input a number."
print mynum
Gives me
Couldn't match expected type ‘IO () -> IO Int’
with actual type ‘IO a0’
The first argument of ($) takes one argument,
回答1:
In
execute conn "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just "Test")
, the left-hand side of the $
operator is execute conn "INSERT…"
, and the right-hand side is MyRecord …
. That is, you're calling execute
with three arguments: the connection, the query, and the parameters. That’s the first problem:
q <- "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just "Test")
res <- execute conn q
Here, the left-hand side of the $
operator is the string "INSERT…"
, and the right-hand side is the parameters. You're trying to call a string, then pass the result as the second argument to execute
.
If q
could be some strange type that represented two arguments, though, it probably wouldn’t be an IO a
. You’re looking to just name a value with let
, not run an action.
This should work:
ipg :: IO ()
ipg = do
conn <- connect defaultConnectInfo { connectHost = "0.0.0.0" }
let query = "INSERT INTO test (num, data) VALUES (?, ?)"
let parameters = MyRecord (Just 200) (Just "Test")
res <- execute conn query parameters
print res
myinput :: IO ()
myinput = do
mynum :: Int <- readLn $ putStrLn "Please input a number."
print mynum
This one just doesn’t make a lot of sense. Maybe it’s a misunderstanding of the $
operator? $
is mostly just a way to write expressions without using parentheses; f $ x
is equivalent to f x
, but the $
has low precedence, so you can write f $ 1 + 2
instead of f (1 + 2)
.
Anyway, I’ll loosely translate it into Python for you, if that helps:
def myinput():
mynum = int(input(print("Please input a number.")))
print(mynum)
If you want to sequence the readLn
and putStrLn
actions, you can use the >>
operator (which is what do
is doing behind the scenes):
myinput :: IO ()
myinput = do
mynum :: Int <- putStrLn "Please input a number." >> readLn
print mynum
That’s not very good for readability most of the time, though. (a >> b
will also discard the result of a
without complaint, whereas do { a; b }
will give a compiler warning if discarding something that isn’t ()
.)
来源:https://stackoverflow.com/questions/39193147/combining-and-splitting-assignment-in-haskell-io-do-block