在 Haskell IO do 块中合并和拆分赋值

Combining and splitting assignment in Haskell IO do block

我/认为/我在两个地方对语言有类似的误解,涉及变量赋值在 do 块中的工作方式,涉及 IO monad。你能帮我理解 (1) 是不是同样的误解,(2) 如何消除它(在回答中,如果你对这个主题有最喜欢的参考资料,也许特别明确)?

我发现当它是一行时我可以成功地执行一个操作,但是当我为了可读性而尝试分成两行时就不行了。

第一部分:将 1 行变成 2 行

为什么这样做有效?

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

但这行不通

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

给我:

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

第一个和第二个尝试将查询部分存储在 q 中的区别。

第二部分:将 2 行变成 1 行

为什么这样做:

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

但这行不通吗?

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

给我

Couldn't match expected type ‘IO () -> IO Int’
            with actual type ‘IO a0’
The first argument of ($) takes one argument,

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

$运算符的左边是execute conn "INSERT…",右边是MyRecord …。也就是说,您使用三个参数调用 execute:连接、查询和参数。这是第一个问题:

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

这里$运算符左边是字符串"INSERT…",右边是参数。您正在尝试调用一个字符串,然后将结果作为第二个参数传递给 execute.

如果 q 可能是某种表示两个参数的奇怪类型,但它可能不会是 IO a。您只想用 let 命名一个值,而不是 运行 一个动作。

这应该有效:

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

这个没有多大意义。也许是对$运算符的误解? $ 主要是一种不使用括号来编写表达式的方法; f $ x等同于f x,但是$的优先级较低,所以可以写f $ 1 + 2而不是f (1 + 2)

无论如何,如果有帮助,我会粗略地为您翻译成 Python:

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

如果你想对 readLnputStrLn 操作进行排序,你可以使用 >> 运算符(这就是 do 在幕后所做的):

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

不过,大多数时候这对可读性来说不是很好。 (a >> b 也会毫无怨言地丢弃 a 的结果,而 do { a; b } 会在丢弃不是 () 的内容时给出编译器警告。)