User login

Navigation

Types are Calling Conventions

If optimization and intermediate languages for lazy functional languages are your things, take a look at Types are Calling Conventions by Max Bolingbroke and Simon Peyton Jones.

It is common for compilers to derive the calling convention of a function from its type. Doing so is simple and modular but misses many optimisation opportunities, particularly in lazy, higher-order functional languages with extensive use of currying. We restore the lost opportunities by defining Strict Core, a new intermediate language whose type system makes the missing distinctions: laziness is explicit, and functions take multiple arguments and return multiple results.

Comment viewing options

This is an exciting direction! I've been thinking recently about guidance of how APIs should be used for optimal performance (esp. in bulk calls, which I'm finding to be a weakness for parallel systems), and have been on the fence over user directed, inferred, or perhaps a bit of both. I'll definitely be keeping an eye out for once they perform some sort of evaluation!

There's an interesting line in the conclusion:
""For a long time, a lazy language
was, to us at least, the obvious intermediate language for a
lazy source language such as Haskell â€“ so it was rather surprising
to discover that an appropriately-chosen strict calculus seems to be
in many ways better suited to the task!""

Urban Boquist did research on efficient compilation of lazy functional languages and devised GRIN, which is a strict, monadic, whole-program functional intermediate language. This language is the backend for the UHC, JHC and LHC haskell compilers.

I haven't read all of the paper by SPJ and Bolingbroke, but it's worth noting GRIN is untyped - JHC uses a variant of GRIN that has types, but we have reverted to using the simple language described by boquist in LHC.

I've been waiting a good while for progress with coexistence of strictness and laziness, as referred to in the final paragraph.

We would like to expose the ability to use â€œstrictâ€ types to the
compiler user, so Haskell programs can, for example, manipulate
lists of strict integers ([!Int ]). Although it is easy to express such
things in the Strict Core language, it is not obvious how to go about
exposing this ability in the source language in a systematic way â€“
work on this is ongoing.

Still unclear exactly how to add laziness to a strict language. For example, do we want a type distinction between (say) a lazy Int and a strict Int?

I've got my own toy language which supports the distinction at its heart, and would like to try a real, useful language that does so. I doubt Haskell is the place for this, since laziness is so entrenched in the libraries.

An old and celebrated analogy says that writing programs is like proving theorems. This analogy has been productive in both directions, but in particular has demonstrated remarkable utility in driving progress in programming languages, for example leading towards a better understanding of concepts such as abstract data types and polymorphism. One of the best known instances of the analogy actually rises to the level of an isomorphism: between Gentzen's natural deduction and Church's lambda calculus. However, as has been recognized for a while, lambda calculus fails to capture some of the important features of modern programming languages. Notably, it does not have an inherent notion of evaluation order, needed to make sense of programs with side effects. Instead, the historical descendents of lambda calculus (languages like Lisp, ML, Haskell, etc.) impose evaluation order in an ad hoc way.

This thesis aims to give a fresh take on the proofs-as-programs analogy---one which better accounts for features of modern programming languages---by starting from a different logical foundation. Inspired by Andreoli's focusing proofs for linear logic, we explain how to axiomatize certain canonical forms of logical reasoning through a notion of pattern. Propositions come with an intrinsic polarity, based on whether they are defined by patterns of proof, or by patterns of refutation. Applying the analogy, we then obtain a programming language with built-in support for pattern-matching, in which evaluation order is explicitly reflected at the level of types---and hence can be controlled locally, rather than being an ad hoc, global policy decision. As we show, different forms of continuation-passing style (one of the historical tools for analyzing evaluation order) can be described in terms of different polarizations. This language provides an elegant, uniform account of both untyped and intrinsically-typed computation (incorporating ideas from infinitary proof theory), and additionally, can be provided an extrinsic type system to express and statically enforce more refined properties of programs. We conclude by using this framework to explore the theory of typing and subtyping for intersection and union types in the presence of effects, giving a simplified explanation of some of the unusual artifacts of existing systems.

If you prefer denotational semantics to structural proof theory, Paul Levy's work on call-by-push-value is the place to look.

The thesis is rough going for me — wish I had time/youth to learn the background to this.

He says,

the answer to the question of eager vs. lazy
evaluation is encoded in types, rather than being a global property of the language.

and from browsing the thesis it appears to me that his answer is successful.

The part of Paul Levy's work that I'm familiar with has a language that supports both call-by-value and call-by-name, but it does not express the duality that is so appealing, and which is present in Zeilberger's thesis.

In browsing the thesis I encountered his description of the primordial function space .

Because the primordial function
space is fundamentally mixed polarity, to get the polarities to work out for call-by-value/call-by-name function spaces we have to insert shifts in different places.

This is something which I have wrestled with on my own. If we had a language in which functions were generally defined with types of the primordial mixed polarity, would they be useful in both call-by-value and call-by-name aspects? What kind of syntactic sugar would be needed? Maybe there's an answer in the thesis which further reading will provide.