Problems in Denotational Semantics: A Few

advertisement
Problems in Denotational Semantics:
A Few Inadequacies in Theory
Gary A. Monroe
UVa – CS655
Spring 99 Semester
February 17, 1999
Introduction
Denotational semantics is a methodology for giving precise meaning to a programming
language. It has traditionally been described as the theory of true meanings for programs, or as
the theory of what programs denote [Jung]. While the syntax of a language is always formally
specified in a variant of BNF, the more important part of defining its semantics is mostly left to
natural language, which is ambiguous and leaves many questions open. In order to reason about a
language or to construct a compiler for this language, a much deeper understanding of semantics
is needed [Schreiner].
In most cases, denotations have been built directly from mathematical theory using
functions and sets. In denotational semantics, a programming language is defined by a valuation
function that maps a program into a mathematical object considered as its denotation (i.e.
meaning). The valuation function of a program construct reveals the essence of this construct
independent of its syntactic surface.
However, there is an alternative viewpoint by which to describe denotational semantics.
Denotational semantics can be seen as a translation from one formal system to another. This
viewpoint has become more and more popular recently, following the rapid progress of the field
of Programming Languages, which results in new and successful language paradigms [Jung].
However, regardless of the view taken, the principles and problems of denotational
semantics are essentially the same. This paper will identify three interactions in programming
languages that denotational semantics are inadequate in dealing with and pose challenges for a
mathematical model. These three computational phenomena are sequential computation,
polymorphism, and mutable state [Jung].
Sequential Computation
[Riecke]
In 1969 Dana Scott defined a purely functional language called PCF (Programming
Computable Functions) as a term language of the logic LCF (Logic of Computable
Functions)[Jung]. In the language description, Scott mentioned a curious function that seemed to
not be definable in PCF:
{ true if d or e = true
por(d,e) = { false if d = e = false
{ __
otherwise
Further work on the problem proved that not only was the “parallel or” function not definable, but
it caused two terms to be distinguished that cannot be distinguished in PCF [Jung]. The two
distinguished terms are “operationally equivalent.” When operational and denotational
equivalence do coincide, the model is called fully abstract [Jung].
The question remains, can one describe the sequential functional computation of PCF via
some abstract denotational model. The problem has fascinated semanticists since 1969. The
problem is robust and fundamental but after 25 plus years of research, there is still no adequate
description of sequential, determinate computation [Jung].
Polymorphism
[O’Hearn]
Polymorphism has posed a severe challenge for semantics from the beginning. Initially
the most substantial issue faced was the impredicativity of the polymorphic lambda calculus, an
extension of typed lambda calculus. Recently the focus has shifted to parametricity, the idea that
a parametric polymorphic function works uniformly for any types to which it is instantiated.
There is a distinction to be made between parametric and ad hoc polymorphism. Ad hoc functions
may work differently at different types, whereas parametric functions are supposed to be uniform.
In the context of the polymorphic lambda calculus impredicativity refers to the non-stratified
nature of types in a language [Jung]. One successful attack on impredicativity is the use of
domain models. The construction works by interpreting types as continuous functors of a certain
kind, allowing a type to be constructed out of finite approximations.
The strongest criticism of domain models is that none of them are parametric, and it is
not clear how they may be modified to be so [Jung]. Types contain junk elements that are nonuniform and contradict the idea of parametric polymorphism. A second problem is that the
domain models can all interpret the polymorphic lambda calculus with a fixed-point operator.
Generally, there is still an unanswered question as to whether traditional domain theory
based on cpo’s (Scott’s model construction generated from base types without top) is compatible
with parametricity, or whether other flavors of domain theory, such as synthetic or axiomatic, are
more appropriate [Jung].
Mutable State
[Stark]
Explicit manipulation of state has been with programming languages from the beginning;
variables and store simply match the registers and memory of the underlying machine. When
using a denotational approach with a standard imperative language, it is not hard to describe an
accurate model of a state that consists of a fixed set of global variables. However, state as a model
of the underlying machine is not at all the same as state as a programming tool [Jung]. Real
applications use explicit state in several more sophisticated ways: private local variables to add
structure and safety to code; variables storing complex objects such as procedures or function
closures; and store that is dynamically created and discarded, to be swept up by a garbage
collector. These are useful features, but their interaction with on another, and other techniques
like higher-order functions, can be subtle and surprising [Jung]. This is reflected in the fact that
good mathematical models are hard to construct, and in some cases it may even be difficult to
find a clearly correct operational semantics.
The problems with denotational semantics come in two degrees [Jung]. First, it may be
hard to find any model at all: as with the storage of functions. Second, a model may not be very
abstract, in that it makes distinctions between too many programs. This shows up because explicit
mutable state is often most useful when its details can be concealed. Local variables are
convenient precisely because they are invisible outside the procedure that uses them; and a memo
function, that caches previous results, should appear externally no different from its non-caching
version [Jung].
Conclusion
The field of denotational semantics is a fairly old one. Since its inception, attempts to
define a convention that could more precisely describe the semantics of a language have pushed
the field of denotational semantics. The basic premise of denotational semantics is the translation
of one formal system into another system namely a mathematical system. This mathematical
system is most often the lambda calculus of the functional programming paradigm. It is intuitive
that anything computable and describable by a high order programming language can be
translated into a mathematical model. However, there are still some aspects of programming
languages that pose some challenges for this transformation and in turn denotational semantics.
Three of these computational phenomena are sequential computation, polymorphism, and
mutable state.
References
[Tennent]
R.D. Tennent. The Denotational Semantics of Programming Languages.
Queen’s University, Kingston, Ontario
[Jung]
A. Jung, M. Fiore, E. Moggi, P. O’Hearn, J. Riecke, G. Rosolini, I. Stark.
Domains and Denotational Semantics: History, Accomplishments and Open
Problems. 1996.
[Schreiner]
Wolfgang Schreiner. Structure of Programming Languages I: Denotational
Semantics. Johannes Kepler University. Linz, Austria
Download