Difference between revisions of "Referential transparency"

Referential transparency is an oft-touted property of (pure) functional languages, which makes it easier to reason about the behavior of programs. While there is no single formal definition[1], a concise one is given by F. Warren Burton[2]:

Referential transparency, the property that an expression always has the same value in the same environment, is central to the mathematical foundation for functional programs.

Side effects like (uncontrolled) imperative update break this desirable property. C and ML are languages with constructs that are not referentially transparent.

As an example, consider the following program[3][4] in Standard ML:

``` puts "h"; puts "a"; puts "h"; puts "a"
```

which prints `"haha"`. In an attempt to factor out the repetition, we write

```let val x = (puts "h"; puts "a")
in  x; x end
```

but now the laugh is on us, because `"ha"` is only printed once. The reason is that `puts`'s side effect is only realized when `x` gets bound, so we should have written

```let fun x () = (puts "h"; puts "a")
in  x (); x () end
```

Haskell's monadic I/O system distinguishes between values and actions like the `puts` procedure above. So we do indeed have that

```putStr "h" >> putStr "a" >> putStr "h" >> putStr "a"
```

is equivalent to

```let x = putStr "h" >> putStr "a"
in  x >> x
```

Notes:

[1] Wolfram Kahl provides a USENET post by Tom DeBoni containing a summary of various definitions for referential transparency.

[2] From Nondeterminism with Referential Transparency in Functional Programming Languages, The Computer Journal, 31(3):243--247, January 1988.

[3] This example is based on one from pages 4-5 of 33, in Philip Wadler's How to Declare an Imperative, ACM Computing Surveys, 29(3):240--263, September 1997.

[4] where `puts` can be defined as `fun puts s = TextIO.output(TextIO.stdOut, s);`

[5] There is some debate about whether the imprecisely-defined semantics of `Int` breaks referential transparency. For instance, `even (maxBound :: Int)` may be `True` in some contexts and `False` in others. Another example is `System.Info.os :: String`.

[6] One perspective is that Haskell is not just one language (plus `Prelude`), but a family of languages, parameterized by a collection of implementation-dependent parameters. Each such language is referentially transparent, even if the collection as a whole might not be. Some people are satisfied with this situation and others are not.