Difference between revisions of "Referential transparency"

From HaskellWiki
Jump to navigation Jump to search
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. I don't think there is any formal definition, but 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.
+
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>
print "h"; print "a"; print "h"; print "a"
+
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 ha = (print "h"; print "a")
+
let val x = (puts "h"; puts "a")
in ha; ha end
+
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>print</code>'s side effect is realized when <code>ha</code> gets bound, so we should have written
+
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 ha () = (print "h"; print "a")
+
let fun x () = (puts "h"; puts "a")
in ha (); ha () end
+
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>print</code> procedure above. So we do indeed have that
+
''actions'' like the <code>puts</code> procedure above. So we do indeed have that
 
<haskell>
 
<haskell>
putChar 'h' >> putChar 'a' >> putChar 'h' >> putChar 'a'
+
putStr "h" >> putStr "a" >> putStr "h" >> putStr "a"
 
</haskell>
 
</haskell>
 
is equivalent to
 
is equivalent to
 
<haskell>
 
<haskell>
let ha = putChar 'h' >> putChar 'a'
+
let x = putStr "h" >> putStr "a"
in ha >> ha
+
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 RT, even if the collection as a whole might not be.
+
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.