Class 11: Introduction to Formal Semantics
Held Wednesday, February 17
Summary
- Reading: Louden, Chapter 12
Contents
Handouts
Notes
- I hadn't intended to talk more about continuation-passing style (CPS),
but I've received enough questions that it clearly warrants at least
a little bit more discussion.
- Cool Statistics Talk tomorrow at 4:15/4:30. Be there, or
never quite understand the meaning of truth (since everything that's
not Bayesian statistics is wrong, at least according to the presenter).
- Next Monday at noon, there's a CS brown bag lunch discussing
summer opportunities for research in CS at Grinnell. (The seniors
may not care, but the rest of you should.)
- Any final questions on the exam?
- In CPS, functions take a continuation as an argument. The
continuation specifies what to do with the result.
- How do you convert a function to CPS?
- First, build a new version of the function that takes a continuation
as an argument and applies that continuation to your results.
;;; Compute the stripes value.
(function (zebra alpha beta)
...
stripes)
;;; Compute the stripes value and then apply a continuation to it.
(function (zebra-cps alpha beta cont)
...
(cont stripes))
- Next, identify any recursive calls to the original function.
(function (zebra-cps alpha beta cont)
(begin
(stuff)
(morestuff (zebra antelope bear))
(evenmorestuff)
(cont stripes)))
- For each recursive call, determine the context of that call (the
continuation)
(lambda (recursive-result)
(begin
(morestuff recursive-result)
(evenmorestuff)
(cont stripes)))
- Convert the recursive call to the original function into a recursive
call to the CPS version.
(function (zebra-cps alpha beta cont)
(begin
(stuff)
(zebra-cps antelope bear (lambda (recursive-result)
(begin
(morestuff recursive-result)
(evenmorestuff)
(cont stripes))))))
- Note that CPS makes more sense if you write and read it in a
particular way.
(foo-cps stuff (lambda (x)
(computation)))
- ``Do foo using stuff. Call the result x. Then do the computation.''
- We'll try this with the Fibonacci function, defined as
(define (fib n)
(if (< n 0) 1
(+ (fib (- n 1)) (fib (- n 2)))))
- When describing a programming language it is necessary to specify not only
the syntax (what programs look like) but also the
semantics (what programs mean).
- Traditionally, semantics have been specified informally. However,
this leads to many ambiguities when the designers don't consider all
the alternatives:
- In many informal definitions, it is unclear in what order
the various function calls are done in
f1()+f2()+f3()
(the order of addition is clear, but it's not always specified which
argument is fully evaluated first).
- In many informal definitions, no effort is made to handle error
cases other than to say that they're in error.
- In many informal definitions, it is unclear how the various parts
of the language interact (can I have a const ref? What does it mean?)
- ...
- Often, it is up to the implementers of languages to make these decisions.
Different implementers make different decisions, which leads to
incompatible implementations.
- Clearly, implementers should be able to make some decisions (e.g., where
in memory to put values). However, most informal definitions don't make
it clear what belongs in the hands of the implementer, and what belongs
in the hands of the language designer.
- What is the solution? Currently, the trend is to define a formal
semantics for the language based on rules of logic.
- Of course, a bad formal specification is no better than a casual
specification.
- The use of logics and formal systems can help us ensure that we
don't make mistakes.
- But it may be hard to read formal notation.
- What are typical informal and formal semantics?
- ``Casual'' semantics: English text describing what each part does.
Likely to lead to misinterpretation, particularly as we concern
ourselves with the interaction of different parts of the language.
- ``Implementational'' semantics: meaning is based on a particular
implementation.
- Translational semantics: meaning is based on a formal translation of
the language to another language. Often, these translations are
done via an attribute grammar. [In some sense, the transitional
semantics is a more formalized implementational semantics based on
compilation.]
- Operational semantics: meaning is given by specifying the effects
of any program on any input. Typically, this is done via an
interpreter. [Similarly, the translational semantics is an
implementational semantics based on interpretation.]
- Denotational semantics: Every program has an associated meaning
(a denotation), given by a Meaning function (the semantics).
For some, this seems similar to translational semantics with
the destination language being something very similar to the lambda
calculus (generic functional programs). The translation is described
more with function definitions than with attributes (although there
is again some similarity). The advantage here is that the destination
is well understood.
History
- Created Tuesday, January 19, 1999 as a blank outline.
- Added details on Wednesday, February 17, 1999.
Some parts were
based on
outline 37 of
CS302 98S.
- Removed uncovered materials on Thursday, February 18, 1999.