Stateful Programming
Despite the virtues of pure functional programming, there are times when you'd like to be able to update memory directly.
Some examples:
Generating unique identifiers.
Manipulating a potentially cyclic graph structure.
Building a histogram of values or doing a bin sort.
Recording global state.
To support such applications, ML includes references and arrays.
References
Think of a reference as a container for a value.
References are created using a special (built-in) data constructor ref, applied to the initial contents of the reference.
The current value of a reference can be fetched using the ! operator.
The current value of a reference can be updated using the := operator.
Note that the type of a reference is formed by applying
the (built-in) parameterized type constructor ref to the type
are:
Examples
We can use refs for anything that an ordinary (imperative language) variable could be used for:
No real advantage over functional code in this case.
Examples (cont.)
A better application: generating unique ids:
First-class references
References are fully first-class values in their own right. This means they can be embedded in data structures, passed as arguments or returned as function results.
For example, references can be used to get the effect of call-by-reference parameters.
Incorrect use of references
Type system prevents us from making the mistake of trying to pass ordinary values:
Updateable dictionaries
Arrays
There is a similar set of constructor and access functions for multi-element arrays, providing constant-time access and update to elements. To use arrays, must first say open Array;
Arrays are created dynamically; their size is fixed only when they are created, and is not part of their type. Array bounds are checked at runtime. Elements are indexed from 0.
There is a similar datatype Vector which allows constant-time access but no update.
Histograms
Doing without built-in state
What would it cost us to insist on being purely functional?
We can simulate the behavior of an n-element state
at the cost of a factor of just .
To see this, view the reference mechanism as a way of associating unique keys (the reference itself) with values (the reference's contents).
The ref
operation invents a new key and pairs it with an initial value.
(We cannot examine the key, but we can compare it with other keys!)
The ! operator fetches the value associated with
a key.
The := operator changes the value associated with
a key.
Viewed as an abstract datatype, this is just a dictionary. It is well-known how to implement dictionaries of size n so that all operations take O(lg n) time.
Arrays are similar, except that array index becomes part of key.