Difference between revisions of "Referential transparency"
m (Minor formatting changes) |
(Various sections rewritten or reorganised) |
||
Line 1: | Line 1: | ||
[[Category:Glossary]] |
[[Category:Glossary]] |
||
− | Referential transparency is an oft-touted property of (pure) functional languages, which makes it easier to reason about the behavior of programs. |
+ | 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[[#notes|[1]]], it usually means that an expression always evaluates to the same result in any context. 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 in Standard ML: |
+ | As an example, consider the following program[[#notes|[2][3]]] in Standard ML: |
<pre> |
<pre> |
||
− | + | puts "h"; puts "a"; puts "h"; puts "a" |
|
</pre> |
</pre> |
||
which prints <code>"haha"</code>. In an attempt to factor out the repetition, we write |
which prints <code>"haha"</code>. In an attempt to factor out the repetition, we write |
||
<pre> |
<pre> |
||
− | let val |
+ | let val x = (puts "h"; puts "a") |
− | in |
+ | in x; x end |
</pre> |
</pre> |
||
− | but now the laugh is on us, because <code>"ha"</code> is only printed once. The reason is that <code> |
+ | but now the laugh is on us, because <code>"ha"</code> is only printed once. The reason is that <code>puts</code>'s side effect is only realized when <code>x</code> gets bound, so we should have written |
<pre> |
<pre> |
||
− | let fun |
+ | let fun x () = (puts "h"; puts "a") |
− | in |
+ | in x (); x () end |
</pre> |
</pre> |
||
Haskell's monadic I/O system distinguishes between ''values'' and |
Haskell's monadic I/O system distinguishes between ''values'' and |
||
− | ''actions'' like the <code> |
+ | ''actions'' like the <code>puts</code> procedure above. So we do indeed have that |
<haskell> |
<haskell> |
||
− | + | putStr "h" >> putStr "a" >> putStr "h" >> putStr "a" |
|
</haskell> |
</haskell> |
||
is equivalent to |
is equivalent to |
||
<haskell> |
<haskell> |
||
− | let |
+ | let x = putStr "h" >> putStr "a" |
− | in |
+ | in x >> x |
</haskell> |
</haskell> |
||
− | ---- |
||
− | This example is taken from |
||
− | * [http://homepages.inf.ed.ac.uk/wadler/ Philip Wadler]. How to declare an imperative. ''ACM Computing Surveys'', 29(3):240--263, September 1997. [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html] |
||
---- |
---- |
||
− | Some definitions of referential transparency are summarized in a USENET post |
||
− | by Tom DeBoni [http://www.cas.mcmaster.ca/~kahl/reftrans.html] |
||
+ | <span id="notes">Notes</span>: |
||
− | ---- |
||
+ | |||
+ | [1] Wolfram Kahl provides a [https://www.cas.mcmaster.ca/~kahl/reftrans.html USENET post by Tom DeBoni] containing a summary of various definitions for referential transparency. |
||
+ | |||
+ | [2] This example is based on one from pages 4-5 of 33, in [https://homepages.inf.ed.ac.uk/wadler/ Philip Wadler]'s [https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.91.3579&rep=rep1&type=pdf How to Declare an Imperative], ''ACM Computing Surveys'', 29(3):240--263, September 1997. |
||
+ | |||
+ | [3] where <code>puts</code> can be defined as <code>fun puts s = TextIO.output(TextIO.stdOut, s);</code> |
||
+ | |||
+ | [4] There is some debate about whether the imprecisely-defined semantics of <code>Int</code> breaks referential transparency. For instance, <code>even (maxBound :: Int)</code> may be <code>True</code> in some contexts and <code>False</code> in others. Another example is <code>System.Info.os :: String</code>. |
||
− | There is some debate about whether the imprecisely defined semantics of <code>Int</code> breaks referential transparency. |
||
− | For instance, <code>even (maxBound :: Int)</code> might to true in some contexts and to false in others. |
||
− | Another example is <code>System.Info.os :: String</code>. |
||
One perspective is that Haskell is not just one language (plus <code>Prelude</code>), but a family of languages, parameterized by a collection of implementation-dependent parameters. |
One perspective is that Haskell is not just one language (plus <code>Prelude</code>), but a family of languages, parameterized by a collection of implementation-dependent parameters. |
||
− | Each such language is |
+ | Each such language is referentially transparent, even if the collection as a whole might not be. |
− | Some people are satisfied with situation and others are not. |
+ | Some people are satisfied with this situation and others are not. |
Revision as of 13:12, 4 January 2021
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], it usually means that an expression always evaluates to the same result in any context. 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[2][3] 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] 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.
[3] where puts
can be defined as fun puts s = TextIO.output(TextIO.stdOut, s);
[4] 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
.
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.