11,758 research outputs found
Modular, Fully-abstract Compilation by Approximate Back-translation
A compiler is fully-abstract if the compilation from source language programs
to target language programs reflects and preserves behavioural equivalence.
Such compilers have important security benefits, as they limit the power of an
attacker interacting with the program in the target language to that of an
attacker interacting with the program in the source language. Proving compiler
full-abstraction is, however, rather complicated. A common proof technique is
based on the back-translation of target-level program contexts to
behaviourally-equivalent source-level contexts. However, constructing such a
back- translation is problematic when the source language is not strong enough
to embed an encoding of the target language. For instance, when compiling from
STLC to ULC, the lack of recursive types in the former prevents such a
back-translation.
We propose a general and elegant solution for this problem. The key insight
is that it suffices to construct an approximate back-translation. The
approximation is only accurate up to a certain number of steps and conservative
beyond that, in the sense that the context generated by the back-translation
may diverge when the original would not, but not vice versa. Based on this
insight, we describe a general technique for proving compiler full-abstraction
and demonstrate it on a compiler from STLC to ULC. The proof uses asymmetric
cross-language logical relations and makes innovative use of step-indexing to
express the relation between a context and its approximate back-translation.
The proof extends easily to common compiler patterns such as modular
compilation and it, to the best of our knowledge, it is the first compiler full
abstraction proof to have been fully mechanised in Coq. We believe this proof
technique can scale to challenging settings and enable simpler, more scalable
proofs of compiler full-abstraction
Simulation in the Call-by-Need Lambda-Calculus with Letrec, Case, Constructors, and Seq
This paper shows equivalence of several versions of applicative similarity
and contextual approximation, and hence also of applicative bisimilarity and
contextual equivalence, in LR, the deterministic call-by-need lambda calculus
with letrec extended by data constructors, case-expressions and Haskell's
seq-operator. LR models an untyped version of the core language of Haskell. The
use of bisimilarities simplifies equivalence proofs in calculi and opens a way
for more convenient correctness proofs for program transformations. The proof
is by a fully abstract and surjective transfer into a call-by-name calculus,
which is an extension of Abramsky's lazy lambda calculus. In the latter
calculus equivalence of our similarities and contextual approximation can be
shown by Howe's method. Similarity is transferred back to LR on the basis of an
inductively defined similarity. The translation from the call-by-need letrec
calculus into the extended call-by-name lambda calculus is the composition of
two translations. The first translation replaces the call-by-need strategy by a
call-by-name strategy and its correctness is shown by exploiting infinite trees
which emerge by unfolding the letrec expressions. The second translation
encodes letrec-expressions by using multi-fixpoint combinators and its
correctness is shown syntactically by comparing reductions of both calculi. A
further result of this paper is an isomorphism between the mentioned calculi,
which is also an identity on letrec-free expressions.Comment: 50 pages, 11 figure
Lazy Evaluation and Delimited Control
The call-by-need lambda calculus provides an equational framework for
reasoning syntactically about lazy evaluation. This paper examines its
operational characteristics. By a series of reasoning steps, we systematically
unpack the standard-order reduction relation of the calculus and discover a
novel abstract machine definition which, like the calculus, goes "under
lambdas." We prove that machine evaluation is equivalent to standard-order
evaluation. Unlike traditional abstract machines, delimited control plays a
significant role in the machine's behavior. In particular, the machine replaces
the manipulation of a heap using store-based effects with disciplined
management of the evaluation stack using control-based effects. In short, state
is replaced with control. To further articulate this observation, we present a
simulation of call-by-need in a call-by-value language using delimited control
operations
Maximal Sharing in the Lambda Calculus with letrec
Increasing sharing in programs is desirable to compactify the code, and to
avoid duplication of reduction work at run-time, thereby speeding up execution.
We show how a maximal degree of sharing can be obtained for programs expressed
as terms in the lambda calculus with letrec. We introduce a notion of `maximal
compactness' for lambda-letrec-terms among all terms with the same infinite
unfolding. Instead of defined purely syntactically, this notion is based on a
graph semantics. lambda-letrec-terms are interpreted as first-order term graphs
so that unfolding equivalence between terms is preserved and reflected through
bisimilarity of the term graph interpretations. Compactness of the term graphs
can then be compared via functional bisimulation.
We describe practical and efficient methods for the following two problems:
transforming a lambda-letrec-term into a maximally compact form; and deciding
whether two lambda-letrec-terms are unfolding-equivalent. The transformation of
a lambda-letrec-term into maximally compact form proceeds in three
steps:
(i) translate L into its term graph ; (ii) compute the maximally
shared form of as its bisimulation collapse ; (iii) read back a
lambda-letrec-term from the term graph with the property . This guarantees that and have the same unfolding, and that
exhibits maximal sharing.
The procedure for deciding whether two given lambda-letrec-terms and
are unfolding-equivalent computes their term graph interpretations and , and checks whether these term graphs are bisimilar.
For illustration, we also provide a readily usable implementation.Comment: 18 pages, plus 19 pages appendi
Lambda Calculus for Engineers
In pure functional programming it is awkward to use a stateful sub-computation in a predominantly stateless computation. The problem is that the state of the subcomputation has to be passed around using ugly plumbing. Classical examples of the plumbing problem are: providing a supply of fresh names, and providing a supply of random numbers. We propose to use (deterministic) inductive definitions rather than recursion equations as a basic paradigm and show how this makes it easier to add the plumbing
Several types of types in programming languages
Types are an important part of any modern programming language, but we often
forget that the concept of type we understand nowadays is not the same it was
perceived in the sixties. Moreover, we conflate the concept of "type" in
programming languages with the concept of the same name in mathematical logic,
an identification that is only the result of the convergence of two different
paths, which started apart with different aims. The paper will present several
remarks (some historical, some of more conceptual character) on the subject, as
a basis for a further investigation. The thesis we will argue is that there are
three different characters at play in programming languages, all of them now
called types: the technical concept used in language design to guide
implementation; the general abstraction mechanism used as a modelling tool; the
classifying tool inherited from mathematical logic. We will suggest three
possible dates ad quem for their presence in the programming language
literature, suggesting that the emergence of the concept of type in computer
science is relatively independent from the logical tradition, until the
Curry-Howard isomorphism will make an explicit bridge between them.Comment: History and Philosophy of Computing, HAPOC 2015. To appear in LNC
- …