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