[FOM] Computer Scientists visa vie Mathematicians - "constructors"

Dennis E. Hamilton dennis.hamilton at acm.org
Tue Jan 28 14:05:22 EST 2020


Lawrence Paulson wrote:

> It might help if more mathematicians were familiar with some basic
> principles of computer science.

Patrik Eklund wrote:

 > I agree, but I would like to add that it also might help if computer 
scientists were familiar with some basic principles in mathematics.

> [...] Mixing languages and bringing in things from the outside is very 
> typical in computer science, but very dubious in mathematics. A typical 
> example of bringing in things from the outside is the function type 
> constructor. Where does it come from? Did it come from the same factory as 
> the types?

I sense a misunderstanding here, and it has to do with the way levels of 
abstraction are managed in computer science and engineering.  I would like to 
unpack that.

First, an apology.

There have been some appropriations of language in computer science and in the 
practice of computer programming that create confusions with respect to 
mathematical notions.  It began with use of "function," "integer," and "real" 
to designate computational notions.  This has continued with use of "type," 
"object," "class," "structure," and even "set."  The replacements "procedure," 
"int," and "float" are preferable but we seem to be stuck with "function."

This sort of collapse/corruption of ideas did not start with programming 
languages.  Engineering and related technical activities had relied on 
mathematical formalisms as expressions interpreted as computations, along with 
the art of numerical analysis, prior to the digital-computer era.   It was 
appealing to the intended developers of technical and engineering computations 
that languages such as 1953's Fortran delivered algebraic-formula and 
trigonometric calculations into the hands of technical users other than the 
heads-down, assembly-language computer programmers of the time.

Considering the popular notion of "maths," it should be no surprise that 
people who become immersed in software development and computer applications 
associate mathiness with their actually-computational notions, because of some 
commonality of expression and nomenclature.  There is, for example, the idea 
of Objects as *distinguished* instances of members of a Class and the 
prevalent conceit that the computational class objects correspond to entities 
in reality.  A sort of Platonic Computerism.

              - - - - - - -

With regard to the layers of abstraction and the apparent bringing things in 
from the outside, a simple view of computation demonstrates what that is 
grounded in.   Consider a function ap(pr, xr) which we can take of having type 
r * r -> r, where r is a denumerable base type, such as strings over some 
alphabet or maybe some other sort of composed but canonically-named data 
structure that can be recorded on persistent electronic storage, read from 
inputs, printed on paper, etc.

Let expression ap(pr, xr) have computational interpretation consisting of 
taking the operand pr as the script for an algorithmic procedure and xr as the 
operand to which that procedure is applied.  We have the same base type, r, 
for representing both scripts and data.  Now consider

applicative expression        ap(ap(ap(sr, fr), gr), xr) = ap(ap(fr, xr), 
ap(gr, xr)).

Here is demonstration of the stored-program principle where scripts and data 
are of the same kind and one can derive new scripts from scripts as data. 
There is a relationship between that expression and the

mathematical expression  s(f, g, x) =  f( x, (g( x) ) also written f(x) (g(x))

where, with respect to -> as application, the evaluation of the applicative 
expression preserves representation of functional type, with x of some type X, 
g of type X -> Y, f of type (X -> (Y -> Z)), and s of type ( ((X -> (Y -> 
Z)) -> ( (X -> Y)  -> (X -> Z) ).   This is an emergent condition, not 
determined by ap itself but by the careful expression of sr, fr, gr, and xr. 
Programming-language compilers make assurances of that and reflect the 
necessary restrictions in the computer program that is produced.

At the ap(pr, xr) level, the evaluation of the applicative expression could 
all be done by rewriting, making a new intended-to-be-a-script result under 
guidance of script pr.  Function such as ap(pr, xr) could be specified in that 
way, and often are.  It is not essential in practice, so long as the result is 
indistinguishable from having done so (and it is engineering of that pragmatic 
validity which bridges the digital computer and mathematics).

Rewriting is avoided in practice by creating forms that are immutable and not 
rewritten, but which are bound to different environments, the contexts of 
individual evaluations.  The bindings are often called closures.  The closures 
are passed as parameters and correctly-connected at the time of operational 
need.  This operates at the time of application of the associated script to 
further operands.  So ap(sr, fr) can be achieved by returning a closure that 
ties to the specific fr for use when needed in an eventual ap(fr, xr), making 
closure fxr that can be used in ap(fxr, ap(gr, xr)), etc.

Notice that we have at the outset a means of representing higher-order 
functional types and it is inherent in the stored programming model of today's 
general-purpose computers.   It should also be clear that "construction" and 
"constructor" are at a different level than the level of expression I just 
characterized.  Similarly, "factories" are at a lower-level of operation, tied 
into the operation of particular programming platforms.  Such facilities are 
part of the internals in the derivation of scripts (computer codes) and their 
execution by directing the attention of the computer's instruction processor. 
Sometimes there a manifestations of construction exposed at the 
programming-language level.  I will say no more on that.

An example of the kind of situation in a higher-level programming notation 
that is resolved using closures (famously called "thunks") is something like 
this:

|  let A(k, c1, c2, c3)
|      = let B() = A(k-1, B, c1, c2)
|          in if k < 1 then c2() + c3() else B()
|   in let x1() = 1,
|             x2() = -1,
|             x3() = 0
|         in A(10, x1, x2, x3)

The definition of functions with nil operands is a computerism for governing 
point of evaluation, distinct from points of definition/mention.  It can be 
taken as  a place where a closure is constructed in the running program, but 
that need not be dealt with explicitly by the programmer.

The reconciliation of bindings is in the c2 and c3 that are involved when 
c2()+c3() is finally evaluated in an instance of B( ) and the instances of B 
those are bound to, their instances of A, etc.  This contrived example is 
derived from an even trickier one by a computer-scientist contemporary who is 
renowned for his reliance on mathematics and who did derive a direct 
closed-form for his version,
<https://en.wikipedia.org/wiki/Man_or_boy_test>.

 - Dennis





More information about the FOM mailing list