Merge and split assignment in a Haskell IO do block

I / think / I have a similar misunderstanding of the language in two places related to how variable assignment works in do blocks, including the IO monad. Could you help me understand (1) this is the same misunderstanding, (2) how to clear it (in the answer and, perhaps, specifically, if you have a favorite link on this topic)?

I find that I can successfully complete the operation when it is all one line, but not when I try to split by 2 for readability.

Part I: Include 1 line in 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 it does 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 attempt to save part of the query in q.

Part II: turning 2 lines into 1

Why does it work:

 myinput :: IO () myinput = do putStrLn "Please input a number." mynum :: Int <- readLn print mynum 

But does it 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, 
0
haskell io-monad
source share
1 answer

IN

 execute conn "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just "Test") 

the left side of the $ execute conn "INSERT…" , and the right side is MyRecord … That is, you call execute with three arguments: join, query, and parameters. This is the first problem:

 q <- "INSERT INTO test (num, data) VALUES (?, ?)" $ MyRecord (Just 200) (Just "Test") res <- execute conn q 

Here, the left side of the $ operator is the string "INSERT…" , and the right side is the parameters. You are trying to call a string and then pass the result as the second argument to execute .

If q could be some weird type that represented two arguments, it probably wouldn't be IO a . You want to just name the value with let , rather than trigger 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 much sense. Maybe this is a misunderstanding of the $ operator? $ is just a way to write expressions without using parentheses; f $ x equivalent to fx , but $ has low priority, so you can write f $ 1 + 2 instead of f (1 + 2) .

In any case, Ill freely translates it into Python for you, if that helps:

 def myinput(): mynum = int(input(print("Please input a number."))) print(mynum) 

If you want to streamline the readLn and putStrLn actions, you can use the >> operator (which is what do does behind the scenes):

 myinput :: IO () myinput = do mynum :: Int <- putStrLn "Please input a number." >> readLn print mynum 

This is not good for readability most of the time. ( a >> b will also discard the result of a without complaint, whereas do { a; b } will give a compiler warning if it discards something that isnt () .)

+4
source share

All Articles