Functional Prog: ML

Functions:
  1. A function is a computation that maps an input domain to an ouput range
  2. In math, the function of a line of slope 2 and y-intercept 3 is f(x) = 2*x + 3
  3. x is an identifier which takes on values in the domain of the function - we call x a parameter of the function f
  4. For a given value of x, f(x) is the value resulting from computing its expression with x being replaced by its value -- e.g., if x is 2 , f(x) is 7 -- we also just simply write f(2)=7
Functions in ML
  1. functions are defined using the fun keyword:
    1. fun square(x:real) = x * x;
  2. because ML is strongly typed, we must tell ML the type of value x will refer to -- hence the need to use the x:real syntax
  3. x is a parameter to the function square -- it is an identifier that refers to a value that the function will use to compute its result
  4. ML responds by telling us square is a function "fn : real -> real" -- what this means is that square takes a real value and results in another real value. The arrow separates the input types (the domain) of the function from the output type (the range)
Defining and Using functions
  1. The definition of a function is done in the fun statement, as above.
  2. A function is used (we say called, or invoked), when it is named in an expression, and values are used in place of each parameter (the values can be existing identifiers, or expressions, but must always be of the correct type).
  3. The use of a function means that the values are used to evaluate the expression in the body of the function, and the computed value is returned to the expression containing the function use.
The scope of parameters
  1. When we use x as a parameter to a function, that use of x only applies to the body of the function
  2. Any other "x" is not affected
Some examples are here

Identifier Scopes and Lifetimes

  1. When an identifier is created and assigned a value to refer to using the val command, it has a certain area of use and a certain lifetime. This area is called its scope.
  2. If we just type in a val statement into ML, that identifier-value binding has a scope of the rest of the ML session, or until the identifier is reassigned a different value.
  3. A parameter of a function has a scope that is just the body of the function. Its lifetime is just one execution of the function body.

The ML stack of identifiers (and function names)

  1. Think of an upward growing stack of identifier-value bindings. Each val statement adds a layer onto this stack.
  2. A reassignment of an existing identifier does not erase the old one. It simply adds a new layer to the stack.
  3. When an indentifier is used in an expression, the stack is searched from top to bottom. The first occurrence of the identifier is the one whose value is used in the expression.
  4. When a function is defined, its name and body is added to the stack as well. Very importantly, any identifier that is in the body that is not a parameter is immediately searched for on the stack and located. It is this identifier-value binding that is used during function execution.
  5. Parameters are viewed as temporary additions to the identifier stack, only there during the function execution. When the function begins (is called), the value passed in as an argument is used to create an identifier-value binding for that parameter. When the function ends, that entry in the identifier stack is erased.
  6. Functions can themselves be redefined! Just like identifiers, they are simply added to the stack, and the old versions are not removed. Previously defined functions using that function name refer to the old binding, because they looked it up immediately when they were defined.

examples found here.

The let...in Statement: Defining temporary identifiers

  1. Function parameters are one type of temporary identifier-value binding.
  2. Another is the let..in statement. It allows you to create temporary identifier-value bindings for use in an expression. Its form is
    1. let
         val id1 = value;
         val id2 = value;
      in
         some expression
      end;

    The identifiers id1, id2, ... are bound to the values specified (these can be expressions, of course), and then the expression after in is evaluated, and the resulting value of this expression is the resulting value of the let...in statement. After that expression is evaluated the bindings created for id1, id2, ... are erased.

The let statement allows you to break apart a computation into separate steps

  1. The examples used used in class are in here, the file let.sml, and the ML session is here.
  2. We used functions to break apart the computation as well, but then the expressions became complex because of the inside-to-out order of evaluation. The first step would be a function innermost in the expression, then the next outermost part of the expression, and on until the whole expression was evaluated. This is shown in the example function circle_area1 in the examples.
  3. The let statement lets us put each of the computation steps in a separate expression, and assign that intermediate step's value to a new identifier. So it is more like how we think when we solve the problem, and it prevents individual expressions from getting too complex. The example function circle_area2 in the examples uses the let statement to make this computation clearer.
  4. the final function in the examples is rect_area, and just shows another use of the let statement

Function definitions, and ML sessions

Tuples

  1. ML has a data type that groups sets of values, called a tuple. A tuple is an ordered set of data values; the values do not have to be the same type. A tuple with three values is called a three-tuple. In general, N values makes an N-tuple.
  2. A tuple is strongly typed, just like simple values are. Each value is a specific type, and the type of the tuple is the ordered types of its values, with a * in between.
  3. This is the same notation for function types
  4. In ML, a tuple is surround by parentheses, and the values are separated by commas
  5. A point on the Cartesian plane can be represented as a two-tuple of two real numbers, like (3.4,5.4), where the first number is the x coordinate and the second is the y coordinate
  6. A function can return a tuple as well -- the body becomes a series of comma-separated expressions, each computing the value for its place in the returned tuple.
  7. Tuples can act as records of data - ("Doe,","John","123 Any St","Any Town","TX",75005)