Комбинирование и разбиение присваивания в блоке Haskell IO do

Я / думаю / у меня похожее неправильное понимание языка в двух местах, связанных с тем, как работает назначение переменных в блоках do, включая монаду IO. Не могли бы вы помочь мне понять (1) это то же недоразумение, (2) как разобраться (в ответе, и, может быть, конкретно, если у вас есть любимая ссылка на предмет)?

Я считаю, что могу успешно выполнить операцию, когда она состоит из одной строки, но не тогда, когда я пытаюсь разделить на 2 для удобства чтения.

Часть I: Превращение 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.

Часть II: Превращение 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,

1 ответ

Решение

В

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)

Если вы хотите упорядочить readLn а также putStrLn действия, вы можете использовать >> оператор (что к чему do делает за кадром)

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

Это не очень хорошо для удобочитаемости в большинстве случаев. (a >> b также откажется от результата a без жалоб, тогда как do { a; b } выдаст предупреждение компилятору, если отбросит что-то, что не ().)

Другие вопросы по тегам