Understanding the "it" variable in GHCi
Introduction
As we are told from the very beginning, Haskell is a purely functional, strongly statically typed programming language.
By purely functional, we mean that everything is mostly immutable. In non-pure languages like C++ or Python, it is perfectly OK to set a variable a
to the value 1
and then to the value 2
later. The following Haskell code does not compile
because GHC complains the “multiple declaration of a
”.
Strongly statically typed, on the otherhand, means that it is not possible to compare a Bool with a Char. What’s more, there is no implicit type coercion in that we cannot use 1
as True
and 0
as False
.
So far, so good.
Until one day, when playing with GHCi we realized that the “magic” variable it
refers always to the value of the last successful expression. The following examples illustrates this behavior.
Prelude> 3
3
Prelude> it
3
Prelude> 'a'
'a'
Prelude> it
'a'
-- We type it again
Prelude> it
'a'
Prelude> True
True
Prelude> it
True
So, what happens?! It seems that not only we can assign values several times to it
but also that the values could have different types!
Dive in
The question seems very natural, yet there are only a few discussion around it on the Internet. What’s more, it is rather difficult to find such discussions because it
is a terrible stop word.
It took me a long time before figuring out why (I once even thought that it
gets some special treatment in the GHCi implementation). Actually, the mechanism is very simple: the shadowing.
Consider the following example (taken from Real World Haskell)
The inner x
in the definition of bar
shadows the outer x
. In particular, though share the same same, they are not the same variable and nothing prevents us from assigning different type values to them (in this example, the outer x
is Num
and the inner x
is String
).
GHCi actually implements an instance of Monad. In a vanilla Monad, we can do very similar shadowing (with do-syntax)
The idea is always the same, the latter x
shadows the former x
and they are two completely different variables. It is easier to see this in the following desugured version
If you actually look at the source code of GHC, what happens is quite similar to what is explained above. Indeed, you can find the following comments
{-
--------------------------------------------------------------------------
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
-}
Basically, when one enters an IO-type expression expr
, the expression is evaluated and its value is extracted and bind to it
Prelude> print 3
-- The expression gets evaluated and () is bond to it, since print 3 :: IO ()
3
Prelude> it
()
On the other hand, when one enters a non-IO type expression expr
, expr
gets evaluated and the result is bond to it
. it
is also printed to screen.
Prelude> 1 + 2
-- The expression gets evaluated and the result 3 is bond to it. `it` also gets printed.
3
Prelude> it
3
Further Reading
The it variable in the Glasgow Haskell Compiler User’s Guide.
The source code where this logic is implemented.