For more down-to-the-code details, this comment in TcRnDriver.lhs might be enlighting:
--------------------------------------------------------------------------
Typechecking Stmts in GHCi
Here is the grand plan, implemented in tcUserStmt
What you type The IO [HValue] that hscStmt returns
------------- ------------------------------------
let pat = expr ==> let pat = expr in return [coerce HVal x, coerce HVal y, ...]
bindings: [x,y,...]
pat <- expr ==> expr >>= \ pat -> return [coerce HVal x, coerce HVal y, ...]
bindings: [x,y,...]
expr (of IO type) ==> expr >>= \ it -> return [coerce HVal it]
[NB: result not printed] bindings: [it]
expr (of non-IO type, ==> let it = expr in print it >> return [coerce HVal it]
result showable) bindings: [it]
expr (of non-IO type,
result not showable) ==> error
So a command at the GHCi prompt can have up to three effects: Some code is evaluated, some things are printed, and some variable names are bound. Your case (the first one in the comment) binds variables, but does not print.
The syntax does resemble the do-notation, so @sinan’s answer is somewhat right, but it is not really what is happening under the hood – otherwise, for example, nothing would ever be printed.