Combining and splitting assignment in Haskell IO do block


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.

<strong>Part I: Turning 1 line into 2</strong>

Why does this work?

ipg :: IO () ipg = do conn <- connect defaultConnectInfo { connectHost = ""} 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 = "" } 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.

<strong>Part II: Turning 2 lines into 1</strong>

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,



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 = "" } let query = "INSERT INTO test (num, data) VALUES (?, ?)" let parameters = MyRecord (Just 200) (Just "Test") res <- execute conn query parameters print res <hr />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:

<pre class="lang-python prettyprint-override">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 ().)


  • CFAutoRelease() -like behavior on iOS6
  • Geometry shader doesn't do anything when fed GL_POINTS
  • Two simultaneous background tasks using NSOperationQueue
  • Different outcomes when using tf.Variable() and tf.get_variable()
  • Increasing dimensions on hover without changing the position of other elements
  • How to calculate % difference of row items in Julia array succinctly
  • Use pnorm from Rmath.h with Rcpp
  • ExtensionDataObject not marked as serializable
  • Is it possible to extract the value from processing-instruction ('define') with XPath?
  • Mongo server accepts credentials from shell, but not from Java/Scala interface
  • Solid Shadow in All Directions
  • How to achieve indepedent multi module versioning with sbt-release plugin
  • Invoke another constructor in the same class
  • Why can't python-colormath convert sRGB to Adobe RGB?
  • Collect HostCPU utilisation and disk I/O speed using XenServer Java API
  • Fortran 90 function return pointer
  • Fraction length
  • Why I can't use uniform1f instead of uniform4f for setting a vec4 uniform?
  • Stitching 2 images (OpenCV)
  • How to resolve this packager error on react native Android
  • Installing PHP 7 on digitalocean
  • Problems installing Yesod for Haskell
  • Mysterious problem with floating point in LISP - time axis generation
  • Display issues when we change from one jquery mobile page to another in firefox
  • Deselecting radio buttons while keeping the View Model in synch
  • MySQL WHERE-condition in procedure ignored
  • JSON with duplicate key names losing information when parsed
  • Convert array of 8 bytes to signed long in C++
  • Do I've to free mysql result after storing it?
  • Jquery - Jquery Wysiwyg return html as a string
  • Return words with double consecutive letters
  • Understanding cpu registers
  • embed rChart in Markdown
  • Recursive/Hierarchical Query Using Postgres
  • Running Map reduces the dimensions of the matrices
  • How to get NHibernate ISession to cache entity not retrieved by primary key
  • Reading document lines to the user (python)
  • How can I use `wmic` in a Windows PE script?
  • Unable to use reactive element in my shiny app
  • Python/Django TangoWithDjango Models and Databases