You are on page 1of 12

Polymonads

Nikhil Swamy?

Gavin Bierman?

Nataliya Guts

University of Maryland, College Park

Abstract
From their semantic origins to their use in structuring effectful
computations, monads are now also used as a programming pattern to structure code in a number of important scenarios, including for program verification, for information flow tracking, to compute complexity bounds, and for defining precise type-and-effect
analyses. However, whilst these examples are inspired by monads
they are not strictly speaking monadic but rather something more
general. The first contribution of this paper is the definition of a
new categorical structure, the polymonad, which explains these notquite-monadic constructions, and subsumes well-known concepts
including monads, layered monads, and systems of monads and
monad morphisms, among others.
We show how to interpret polymonads in the setting of System F. While this interpretaion enables several useful programming idioms, programming directly with polymonads in F is far
from convenient. Addressing this concern, the second contribution
of this paper is core ML-like programming language, PM, with a
polymorphic type inference algorithm that infers a principal polymonadic type for a program and elaborates it to F in a coherent
wayall unambiguous typing derivations produce elaborated programs with the same semantics. The resulting programming style
in PM is powerful and lightweight, as we demonstrate through a
series of examples.

1.

Introduction

To a programmer, a monad is an abstract datatype represented by a


unary type constructor M and three operations, map, bind and unit
(a.k.a., return), with the following signature:
map: a b. (a b) M a M b
bind: a b. M a (a M b) M b
unit: a. a M a

Implementations of these operations are expected to obey the


monad laws, which enable reasoning about transformations of
monadic programs, both by programmers and by tools such as
optimizing compilers [26].
Since the time that Moggi first connected monads to effectful
computation [17], monads have proven to be a surprisingly versatile
computational structure. Perhaps best known as the foundation of
Haskells support for state, I/O, and other effects, monads have also
been used to structure APIs for libraries that implement a wide
range of programming tasks, including parsers [11], probabilistic
computations [21], and functional reactivity [7, 3].
While conceptually simple, programming directly against a
monadic API is impractical. Programmers must insert calls to bind
and unit pervasively, and when composing multiple monads, morphisms between the monads must also be inserted. However, effective type inference algorithms have been devised to reduce this
burden. In the context of Haskell, monadic type inference relies
on the mechanisms of typeclasses and a specialized syntax (the do
notation) to infer the placement of binds and units. For ML, our

Michael Hicks

Daan Leijen?

Microsoft Research

own prior work [24] has shown that the existing let-structure of a
call-by-value program can be used to infer the placement of the
morphisms in addition to the binds and units.
The monadic programming pattern is sufficiently appealing that
many researchers have developed subtle variations to adapt and apply monads to new problem domains. Examples include Wadler
and Thiemanns [27] indexed monad for typing effectful computations; Atkeys parameterized monad [2], which has been used to
encode disciplines like regions [13] and session types [20]; Devriese and Piessens [6] monad-like encodings for information flow
controls; Danielssons [5] counting monad for computational complexity; and many others. Oftentimes these extensions are used to
prove stronger properties about computations than would be possible with monads, or to prevent undesirable behavior (such as illegal
information flows, memory errors, etc.).
We observe that each of these monad-like patterns involves a
(possibly infinite) family of abstract datatypes M = {M1 , M2 , . . .}
with map operations available at every Mi . However, unlike for traditional monads, a unit operation is provided for only some elements of M, and instead of a bind for each Mi , a bind-like operator with signature a b. Mi a (a Mj b) Mk b is given for
some subset of triples (Mi , Mj , Mk ) in M3 . Additionally, an operation morph:a. Mi a Mj a may be provided for some elements
(Mi , Mj ) in M2 .
This got us wondering: is there some underlying structure
that governs the behavior of these monad-like families of abstract
datatypes? Can one reason about programs using these types with
principles akin to those for monads? Are there effective type inference algorithms that make it simple to program with these types?
In this paper we answer these questions in the affirmative
through the development of a new categorical structure we call
a polymonad (2.1). A polymonad can be characterized simply by
a set of (map:a b. (a b) M a M b) and (join:a. Mi (Mj a)
Mk a) operations on a family of abstract datatypes. The other operations, including bind, unit and morph, can all be expressed as
derived forms. We identify polymonad laws, from which the laws
for monads and monad morphisms can be derived as special cases.
In 2.2, we explain our categorical foundations in the context of
System F [9] and show how the polymonad laws yield principles
that justify the reasoning that programmers routinely conduct on
(and have come to expect of) their programs.
In 3 we give a detailed example of programming with polymonads: information flow security. Whilst demonstrating the feasibility and promise of polymonadic programming, we see that it
can be somewhat cumbersome (this is just the analogue to similar
problems when programming directly with monads [24]). To ease
this task, 4 presents PM, an ML-like source language, and 5
gives a type inference and elaboration algorithm for PM along the
lines of our previous work for monads [24]. We show that our algorithm computes principal types (Theorem 6 and Theorem 8), and
elaborates PM programs to well-typed F programs (Theorem 9).

2013/3/29

This much of our type inference machinery could just as well


have been carried out in Haskell, taking advantage of its typeclass
mechanism [12]. However, when using Haskell, a difficulty that
comes up immediately is that the inferred principal types for common polymonadic programs must often be rejected as ambiguous.
Fortunately, in PM, knowing the polymonad laws, we are able to
prove that many of those programs with principal types rejected by
Haskell are actually unambiguous, and that all the elaborated F
programs produced by our algorithm have the same semantics, i.e.,
type inference is coherent (Theorem 15).
Armed with our coherence result, we develop several strategies
for simplifying and solving the constraints generated by type inference (6). In many cases, constraints can be simplified by appealing to the polymonad laws, without loss of generality. In other
cases, particularly for recursive programs, constraint solving is an
undecidable problem. This should not be surprising; polymonads
include constructions such as the Hoare monad [18], which can express the functional correctness of programsbeing able to solve
constraints for recursive programs would amount to the automatic
discovery of inductive invariants for proofs of program correctness.
Neverthless, coherence ensures the applicability of ad hoc solving strategies (e.g., those that may rely on sophisticated theorem
provers to discover invariants), since all valid solutions will result
in semantically identical elaborated programs.
As a final contribution, in 7, we discuss PM, an extension
of PM with indexed types, a feature commonly available in fullfledged language implementations. Using these indexed types, we
present several useful examples of polymonads, including an encoding of Atkeys parameterized monads and session types [2, 20],
a new encoding of multi-level information flow controls, and a new
encoding of contextual type and effect systems [19]. A prototype
implementation of PM is available on the web. It has been used to
infer types for all the examples in this paper.

2.

RST
R3

RV

A monad on a category consists of a endofunctor and a pair of natural transformations involving that endofunctor satisfying a number of conditions [15]. As mentioned in the introduction, we have
come across a number of monad-like programming patterns that,
in contrast, involve a number of endofunctors and natural transformations between them. This leads to the following rather compact
definition.
Definition 1 (Polymonad). A polymonad over a category C is
a pair of a set M of endofunctors on C and a set J of natural
transformations:

/ UT
2

/W

It is important to observe that this definition is not equivalent to


simply a collection of monads (connected, perhaps, by monad morphisms). A functor M M may, or may not, be pointed (meaning
II M J) and may, or may not, have a multiplication (meaning M M M J). The definition of a polymonad is considerably more general. However, let us reassure the reader immediately
with the following theorem.
Theorem 2. Every monad, (R, R , R ), is a polymonad.
Proof. We take as our collection of endofunctors the set {R, I}
and as our collection of natural transformations the set {R : II
R, R : RR R, id : RI R, id : II I}. It is simple to see
that the three conditions of Definition 1 hold.
Interestingly, we can prove the opposite direction.
Theorem 3. A polymonad ({R, I}, {R : II R, R : RR
R, id : RI R, id : II I}) gives rise to a monad (R, R , R ).
Proof. The left unit law for a monad is given by the following
diagram that commutes by the associativity requirement of the
polymonad.
IIR = IR
Iid

IR

A categorical account of polymonads

1 T

Foundations

This section formally defines a polymonad using category theory,


and then shows how this definition can be interpreted in a programming language. This section can be safely skipped on a first read.
2.1

3. Associativity: For all {R, S, T, U, V, W } M,


if {1 , 2 , 3 , 4 } J, then the following diagram commutes:

/ RR


id

/R

The right unit law holds similarly. The associativity law of the
monad is clearly given by the associativity requirement of the
polymonad.
In fact, polymonads support more general versions of the familiar
monad laws. Given a polymonad (M, J), we refer to a map R :
II R J as a unit for R. We also refer to a map : RI
S J as a morphism from R to S. We can show that polymonads
support generalizations of the monad morphism and monad unit
laws.
Theorem 4 (Generalized monad laws). Let (M, J) be a polymonad.

M = {M | M : C C}
J = { : M N P | M, N, P M}

These must satisfy the following conditions (where we write M N


P J to mean . : M N P J)
1. Identity:
(i) I M is the identity functor and
M I = IM = M for all M M.
(ii) for all M M, M I M J is an identity map
(equivalently, M M J).
2. Diamond: For all {R, S, T, W } M, U M such that
{RS U, U T W } J if and only if V M such that
{ST V, RV W } J.
2

1. For all : RI S, R : II
R, S : II S J the following
diagram commutes:

/R
 

2. For all 1 : M I

M 0 , 2 : N I N 0 , 3 : LI
L0 , 1 : M N L, 2 : M 0 N 0
L0 J, the following diagram
commutes:

MN
1

1 2

/ M 0N 0

/ L0

2013/3/29

3. For all : II R, : RS
T, : SI T J the following
diagram commutes:

functor set
bind set
bind type

/ RS


/ RS
! 

| M, M
| b:,
(M1 , M2 )  M

let bindPQR : a b. P a (a Q b) R b =
a b.p q. joinPQR b (mapP a (Q b) q p)

Alternatively, we can define polymonads where the unit and monad


morphisms are distinguished from the multiplication natural transformations. We sketch this definition in our supplementary document and leave further category theory to future work.
We conclude by giving some further examples of polymonads.
Theorem 5. 1. Any two monads, (R, R , R ) and (S, S , S ) on
a category C and a monad morphism, : R S, between them
give rise to a polymonad.
2. Let R be an endofunctor over C, (S, S , S ) a monad over C,
and : R S be a natural transformation. These give rise to
a polymonad.
2.2

::=
::=
::=

Figure 1. A bind-centric polymonadic signature

T
4. For all : II S, : RS
T, : RI T J the following
diagram commutes:

Polymonads in System F

A polymonad (M, J) can be interpreted in the category of System


F, thus establishing a foundation for its use in programming. The
interpretation follows a familiar route.

To see this, consider the following function, which composes


three computations r, s, and t, where the latter two are each parameterized by the result of the previous computation:
let f : a b c. R a (a S b) (b T c) W c =
a b c. r s t. bindUTW b c (bindRSU a b r s) t

Inlining the binds, and appealing to the laws (in particular, that the
joins are natural transformations), one can show that f is equivalent
to the code below:
let f = a b c. r s t. (joinUTW c joinRSU (T c))
(mapR (S b) (S (T c)) (mapS b (T c) t) (mapR a (S b) s r))

If f is definable, then the Diamond law ensures that there exists a


type constructor U such that the function g below is also definable,
and the Associativity law (cast as equation (4) above), ensures that
it is equivalent to f.
let g = a b c. r s t. (joinRVW c mapR (S (T c)) (U c) (joinSTV c))
(mapR (S b) (S (T c)) (mapS b (T c) t) (mapR a (S b) s r))

Interpreting functors and joins Each endofunctor M M


is represented by a unary type constructor M and a function
mapM: a b. (a b) M a M b. These are expected to satisfy
the functor laws below.

Fortunately, appealing to the laws again (this time using the fact
that maps distribute over function composition), we can prove that
g is equivalent to the function g below, which, like f, uses binds for
sequencing, although in a different order. However, our reasoning
shows that the order does not influence the semantics.

(1) a. mapM a a (x:a.x) = a. x:M a. x

let g = a b c. r s t. bindRVW a c r x:a. bindSTV b c (s x) t

(2) a b c. (f:b c) (g:a b). mapM a c (f g) =


a b c. (f:b c) (g:a b). mapM b c f mapM a b g

Each :P Q R J is represented as an F function joinPQR:


a. P (Q a) R a, and satisfy the natural transformation law below.

Thus, pleasantly, the familiar Kleisli-style bind operators work for


polymonads as well. As this form of operator is dominant in the
world of monadic programming, we will adopt it for the rest of this
paper.

3.

(3) a b.f:a b. mapR a b f joinPQR a =


a b.f:a b. joinPQR b mapP (Q a) (Q b) (mapQ a b f)

Interpreting the polymonad laws. The Identity law requires that


the identity functor I M be represented as the F type function
Id a = a together with the map mapId a b f = f. The Diamond law
ensures that that the appropriate joins exist in the F interpretation,
while the Associativity law translates to the following equation:
(4) a. joinRVW a mapR (S (T a)) (V a) (joinSTV a) =
a. joinUTW a joinRSU (T a)

Programming with binds. With this interpretation in place, one


can program with maps and joins. However, we observe that in the
context of monads it is often more convenient to program with a
Kleisli-style bind operator, defined in terms of maps and joins,
both because composing computations using bind is syntactically
lightweight, and because the monad laws require that bind associates, ensuring common program transformations (e.g., inlining) are semantics preserving. Pleasingly, as we now show, polymonadic binds can be similarly defined and provide the same convenience as their monadic counterparts.
For each joinPQR in the interpretation, we can define an operator bindPQR as follows:

Programming with polymonads

This section presents a detailed example to give a flavor of programming with polymonads, showing how they can be used to enforce information flow security [23] in a language with mutable
references. This polymonad will serve as both motivation for and
illustration of the development of the next several sections. Section 7 develops several additional examples of polymonads.
When programming with polymonads we use the bind-oriented
view, employing the notion of a polymonadic signature defined
in Figure 1. A signature is a pair (M, ), where each element
M of functor set M is a type constructor representing a polymonadic functor, and each element of bind set is a bind operator
bindPQR: a b. P a (a Q b) R b, for some functors P, Q, R in
M. We abbreviate the type of bindPQR as (P, Q)  R. When
programming with polymonads we will often use binds having type
(M, Id)M0 ; these play a role similar to morphisms when programming with normal monads, so we write M , M0 as a shorthand.
We also write (P, Q)  R to mean b. b:(P, Q)  R .
3.1

Example: Polymonadic information flow controls

Several researchers have proposed type systems or libraries with


a monad-like structure to implement information flow controls [6,
22, 16, 4, 1]. These controls allow a programmer to indicate that
some program inputs are secrets, and that some outputs are public.

2013/3/29

Signature

computation from leaking information about its l1 -secure -typed


argument into a reference cell that is less than l1 -secure. Dually,
the constraints l1 v l3 and l2 v l3 ensure that the -typed result
of the composed computation is at least as secure as the results of
each component. The final constraints p3 v p1 and p3 v p2 ensure
that the write label of the composed computation is a lower bound
of the labels of each component.
We can implement the readl, writel and various binds as we
would a normal state monad; e.g., a computation IST p l would
be implemented as a function of type heap ( heap) where heap
implements the mapping from pointers to values. The limitation on
allowed binds is merely there to statically prevent illegal flows, and
has no run-time effect.
Using these definitions, we can write the program given at the
bottom of the figure. This function takes a secret reference savings,
and a non-secret reference, interest, and modifies the former by
adding to it the latter if it is non-negative. (This example is a bit
contrived for illustration purposes which will be clear later.) The
first bind sequences the read interest with the sequel, which carries
out the update. Each branch of the conditional is a computation of
type IST H H (); in the true branch a read of savings composed
with the computation that writes the final result to it already has this
type; while the else branch has to be lifted with unit HH applied
to the identity function.

M = {IST L L, IST L H, IST H L, IST H H}


= {bId : Id , Id}
{unit pl : Id , IST p l | p, l {L, H}}
{map pl : IST p l , IST p l | p, l {L, H}}
{app pl : (Id, IST p l)  IST p l | p, l {L, H}}
{b p1 l1 p2 l2 p3 l3 : (IST p1 l1 , IST p2 l2 )  IST p3 l3 |
p1 , p2 , p3 , l1 , l2 , l3 {L, H}
l1 v p2 l1 v l3 l2 v l3 p3 v p1 p3 v p2 }
Auxiliary functions
readL
readH
writeL
writeH

:
:
:
:

intref
intref
intref
intref

L IST H L int
H IST H H int
L int IST L L ()
H int IST H L ()

Sample program
let add interest : intref H intref L IST H H () =
savings. interest.
b HL HH HH (readL interest) (currinterest.
if currinterest > 0 then
b HH HL HH (readH savings) (currbalance.
let newbalance = currbalance + currinterest in
writeH savings newbalance)
else unit HH () (x.x)

Figure 2. Signature and implementation for polymonad IST

3.2

The goal is to ensure that the public outputs are independent of the
secret inputsa property called noninterference [10].
Figure 2 presents a polymonad IST, which implements a heap,
like the standard ST state monad, but prohibits programs that would
leak information. Conceptually, inputs and storage cells are labeled
with security labels l {L, H} which form a lattice with order
L < H, meaning that data labeled H is more secret than data
labeled L. A program is acceptable if data labeled H cannot flow,
directly or indirectly, to computations or storage cells labeled L.
ISTs functor set M consists of four type constructors. Intuitively, a computation with type IST p l potentially writes to references labeled p and returns a -result that has security label l; we
call p the write label and l the output label. For instance, IST H L
is the type of a computation that may write to secret storage cells
while computing a public value. We also introduce types for integer
references: intref H and intref L classify references to an integer
value labeled H and L, respectively. We can then give functions for
reading and writing references, whose types are given in the second
portion of the figure (we elide allocation functions for simplicity).1
Each return type has form IST p l . Function readH has l = H
since the returned -value is sensitive, but in all other cases it is
not. Function writeL has p = L, since a write is occurring to a lowsecurity cell, but in all other cases it is not. (Thus, H is the most
permissive write label, and L is the most permissive output label.)
The allowed binds consist of units unit pl, which when composed with the identity function lifts a normal term into an IST
computation; identity maps map pl and the related functions
app pl (which, as is true of maps and apps in general, can be
implemented as app pl = x.f.map pl (f x) (x.x)). The
remaining binds are those satisfying the given conjunction of inequalities which enforce proper information flow. When composing
a computation IST p1 l1 with a function IST p2 l2 , the
type of b p1 l1 p2 l2 p3 l3 requires l1 v p2 to prevent the second
1 A more elegant formulation would be to have functors IST that are param-

Better programmability

While programming directly with polymonads, as done for the program given in Figure 2, is feasible, it is by no means easy. First of
all, it is tedious to compose the various binds with the actual program logic, and the result is painful to read. Even worse, in ML
this code is pointless: the state monad is built into the language!
We could have implemented IST on top of standard references and
implemented the various binds as identity functions, in which case
their use is not to add functionality, but rather to enforce the information flow property. Finally, sometimes the most straightforward
insertion of binds leads to less reusable code: sometimes it makes
sense to parameterize a function by the binds it uses, rather than
inlining them directly.
We can solve all of these problems by implementing type inference with elaboration [24]. The goal is to allow the programmer to
write the program in a direct style, treating computations of type
M as if they were values of type . Then the compiler will perform type inference to infer a principal type for the program, and at
the same time rewrite it to insert the necessary, and most general,
polymonadic binds. In the next section, we present a type inference
algorithm that can produce a program like the one in Figure 2 by
performing inference on the following (more readable) program:
let add interest = savings. interest.
let currinterest = readL interest in
if currentinterest > 0 then
let currbalance = readH savings in
let newbalance = currbalance + currinterest in
writeH savings newbalance
else ()

Then, in Section 5, we prove that all such rewritings that the compiler could perform are coherent, i.e., they will have the same runtime semantics. Sections 6 and 7 present extensions and improvements to the basic approach.

4.

Type inference for PM

eterized by their labels (i.e., l {H , L} could be type indexes) and reference functions whose types are likewise parameterized over labels. Section 7 shows how to generalize PM to support type indexes, and presents
the generalization of IST along with several other examples.

Figure 3 presents the syntax of PM, an ML-like, call-by-value language with a type system that enables a lightweight form of polymonadic programming. PM programmers author programs in direct style, using the standard notation for -abstractions, function

2013/3/29

values
expressions

v
e

functors
value types
type schemes
bind type
constraint bag

::=
::=
|
::=
::=
::=
::=
::=

x | c | x.e
v | e1 e2 | let x = e1 in e2
if e then e1 else e2 | letrec f = v in e
|M
a | T | 1 m 2

a.P
(m1 , m2 )  m
| , P

argument using two bind operators, and then apply the function.
(TS-If) is also similar, since we sequence the expression e in the
guard with the branches. As usual, we require the branches to
have the same type. This is achieved by generating morphism
constraints, m2 , m and m3 , m to coerce the type of
each branch to a functor m before sequencing it with the guard
expression.
4.2

Figure 3. PM: Terms and types (extends Figure 1)


application, conditionals, and (possibly recursive) let-bindings. A
PM program is interpreted in the context of a bind-oriented polymonadic signature (M, ). We develop a type inference algorithm
(in this and the next section) that computes a principal type for a
PM program and elaborates it to F (extended with recursion) by
inserting bind operations from in a systematic way.
In order to facilitate inference, we choose a type language for
PM that bears close resemblance to the types in our previous work
on monadic programming in ML [24]. Specifically, we separate
value types from computation types (m ). Values v are given
types , including value-type variables a, type constants T , and
functions with value types in their domain and computation types
in their co-domain. Non-value expressions (i.e., computations) are
always given type m , where m is either a functor M from M,
or a functorial type variable . A value v can also be given a
polymorphic type scheme =
a.P that generalizes over
both kinds of type variables, and abstracts over P , the set of bind
operations required for the elaboration of v to be well-typed.
4.1

A syntax-directed type inference system

Figure 4 shows a syntax-directed type system for PM, organized


into two main judgments. The value-typing judgment P | ` v :
types a value v in an environment (binding variables x and constants c to type schemes) at the type , provided the constraints P
are satisfiable. The expression-typing judgment P | ` e : m
is similar, except that it yields a computation type. Constraint satisfiability is defined by P |= P 0 , which states that P 0 is satisfiable
under the hypothesis P if P 0 P .
The rule (TS-XC) types a variable or constant at an instance of
its type scheme in the environment. The instance relation for type
schemes P |= is standardit simply instantiates the bound
variables, and checks that the abstracted constraints are entailed by
the hypothesis P . The rule (TS-Lam) is straightforwardnote that
the bound variable is always given a value type and the body a
computation type.
The rule (TS-V) allows a value v : to be used as an expression
by lifting it to a computation type m , so long as there exists
a morphism (or unit) from the Id functor to m. (TS-Rec) types a
recursive let-binding by typing the definition v at the same (mono)type as the letrec-bound variable f . When typing the body e, we
generalize the type of f using a standard generalization function
Gen(, P ), which closes the type relative to by generalizing
over its free type variables. (TS-Let) is similar, although somewhat
simpler since there is no recursion involved.
(TS-Do) is best understood by foreshadowing our elaboration
algorithm from 5. Since we are in a call-by-value setting, we
interpret a let-binding as forcing and sequencing two computations.
That is, we give let x = e1 in e2 the type m2 2 and elaborate it
to bind 1 2 e1 x:1 .e2 , so long as e1 can be typed at m1 1 and
elaborated to e1 ; e2 can be typed at m2 2 and elaborated to e2 ;
and, bind has the type (m1 , m2 )  m3 .
(TS-App) is similar to (TS-Do), where, again, since we use
call-by-value, in the elaboration we sequence the function and its

Principal types

The type rules admit principal types, and there exists an efficient
type inference algorithm that finds such types. The way we show
this is by a translation of polymonadic terms (and types) to terms
(and types) in OML [12] and prove this translation is sound and
complete: a polymonadic term is well-typed if and only if its translated OML term has an equivalent type. OMLs type inference algorithm is known to enjoy principal types, so a corollary of our
translation is that principal types exist for our system too.
We encode terms in our language into OML as shown in Figure 5. We rely on four primitive OML terms that force the typing of
the terms to generate the same constraints as our type system does:
ret for lifting a pure term, do for typing a do-binding, app for typing an application, and if for conditionals. Using these primitives,
we encode values and expressions of our system into OML.
We write P | `OML e : for a derivation in the syntax directed
inference system of OML (cf. Jones [12], Fig. 4).
Theorem 6 (Encoding to OML is sound and complete).
Soundness: Whenever P | ` v : we can also derive P | `OML
JvK? : in OML. Similarly, when P | ` e : m we have
P | `OML JeK : m .
Completeness: If we can derive P | `OML JvK? : , there also exists a derivation P | ` v : , and similarly, whenever P | `OML
JeK : m , we also have P | ` e : m .
The proof is by straightforward induction on the typing derivation of the term. It is important to note that our system uses the
same instantiation and generalization relations as OML which is
required for the induction argument. Moreover, the constraint entailment over bind constraints also satisfies the monotonicity, transitivity and closure under substitution properties required by OML.
As a corollary of the above properties, our system admits principal
types via the general-purpose OML type inference algorithm.
4.3

Ambiguity

One might then think that we could simply infer types for PM by
translating programs to Haskell, which uses OMLs inference algorithm. However, this strategy is unsatisfactory, as Haskell would
reject many useful programs. Translated to our setting, Haskell rejects as ambiguous any term whose type
a.P includes a
variable such that is free in P but not we call such variables open. Haskell, in its generality, must reject such terms since
the instantiation of an open variable can have operational effect,
while at the same time, since the variable does not appear in , the
instantiation for it can never be uniquely determined by the context
in which the term is used.
Rejecting all types that contain open variables would be unacceptable for PM. Many extremely simple terms have principal types that contain open variables; e.g., f x.f x has the type
a b m m m. (Id,m)m, (Id,m)m (a m b) a m b,
which contains the open variable m. However, with our knowledge of the semantics of polymonadic binds, we can prove that
such programs are indeed unambiguousthis is the development
of the next section.

2013/3/29

= [
/
a][m/
] P |= P1
P |= (
a. P1 ) >

(TS-Inst)

P | ` v :

v {x, c}
P |= (v) >
P | ` v :

(TS-XC)

P | ` e : m

P | ` v :
P, Id , m | ` v : m

P |= >

P1 | ` v :

P | , x:Gen(, P1 ) ` e : m 0

P | ` let x = v in e : m 0
P | ` e1 : m1 1

P | ` e1 : m1 bool

P | , f :Gen(, P1 ) ` e : m 0

(TS-Rec)

(TS-Let) where Gen(, P1 ) = (ftv(P1 ) \ ftv()).P1


P |= (m1 , m2 )  m3

P | ` e2 : m2 2
P |= (m1 , m4 )  m5
P | ` e1 e2 : m5

P | ` e2 : m2

(TS-Lam)

P | ` letrec f = v in e : m 0

P | , x:1 ` e2 : m2 2
e1 6= v
P | ` let x = e1 in e2 : m3 2

P | ` e1 : m1 (2 m3 )

P | , x:1 ` e : m 2
P | ` x.e : 1 m 2

P1 | , f : ` v :

(TS-V)

P 0 . P
P |= P 0

P |= P 0

P | ` e3 : m3

(TS-Do)

P |= (m2 , m3 )  m4

(TS-App)

P |= m2 , m, m3 , m, (m1 , m)  m0

(TS-If)

P | ` if e1 then e2 else e3 : m0

Figure 4. Syntax-directed type rules for PM (the signature is an implicit parameter)

ret
do
app
if

: . (Id , )
: 1 2 . ((1 , 2 )  )
1 ( 2 )
: 1 2 3 4 . ((1 , 4 )  , (2 , 3 )  4 )
1 ( 3 ) 2
: 1 2 3 0 . (2 , , 3 , , (1 , )  0 )
1 bool (() 2 ) (() 3 ) 0

JxK?
JcK?
Jx.eK?

=x
=c
= x.JeK

JvK
Je1 e2 K
Jlet x = v in eK
Jlet x = e1 in e2 K
Jif e1 then e2 else e3 K
Jletrec f = v in eK

= ret JvK?
= app Je1 K Je2 K
= let x = JvK? in JeK
= do Je1 K Jx.e2 K?
(with e1 6= v)
= if Je1 K ().Je2 K ().Je3 K
= letrec f = JvK? in JeK

Figure 5. Translation of PM to OML

5.

monads. Instead, we use the occurrences of unification variables in


the computed constraints to precisely reconstruct the shape of the
inserted binds. In order for this to work, we have to carefully separate unification constraints from bind constraints. With this separation, we prove that the computed bind constraints can be viewed
as a directed acyclic graph (DAG), and using this fact we prove
that type inference is coherent. The next definition shows how to
construct a directed graph corresponding to a bag of constraints.
Definition 7 (Graph-view of a constraint-bag P ). A graph-view
G(P ) = (V, E, A) of a constraint-bag P is a set of vertices V ,
a set of directed edges E, and a vertex assignment A : V m,
where:
The vertex set V

= {.0, .1, .2 | P }, i.e., each


constraint contributes exactly three vertices to V , one for each
of its functor arguments.
The assignment A records the functor value of each vertex, such
that .i V.A(.i) = mi when = (m0 , m1 )  m2 .
The set of directed edges
E

Algorithmic type inference and coherence

In this section, we present a type inference and elaboration algorithm for PM. We prove our algorithm sound and complete with
respect to the syntax-directed system of Figure 4 (by proving it
sound and complete with respect to OMLs algorithmic judgment).
We also prove that our algorithm elaborates well-typed PM programs to well-typed F programs, and, most importantly, that all
unambiguous elaborations are coherent. Thus, programmers can reliably view our syntax-directed system as a specification without
being concerned with the details of how programs are elaborated.

Notation We make use of a pictorial notation for constraint-bags


to evoke their graph view. Specifically, we represent the constraint
(m1 , m2 )  m3 and m1 , m2 as the two left-most diagrams
below, respectively. Given a pair of constraints and 0 such that
we have an edge from (.2, 0 .i), we simply identify these vertices
in our pictorial notation, rather than explicitly showing an edge.
For example, the pair of constraints (m1 , m2 )  , (m3 , )  m4
is drawn as the right-most picture below.

m3

Algorithmic type inference for PM

5.1

{(.0, .2), (.1, .2) | P }


{(.2, 0 .0) | . = A(.2) = A( 0 .0)}
{(.2, 0 .1) | . = A(.2) = A( 0 .1)}

m2

m1

?

m2

m3


m4

m1

We start by presenting our type inference and evidence-passing


elaboration algorithm. The general structure of this algorithm is the
same as OMLs. The difference is that, for purposes of proving coherence, our rules permit us to recover of the structure of the elaborated binds from the constraints. In our previous work [24], we
employed constraint bundles to group related constraints in our
coherence proof, but we find that technique inadequate for poly-

With our graph-view of constraints in mind, we turn to describing the algorithm in Figure 6. The judgment has the following shape, where the subscripts indicate inputs and outputs:
in | out | Pout | in `rin ein : tout ; eout . Informally, given a substitution in of the free variables in the context in , and given some

2013/3/29

m1

m2

in | out | Pout | in `rin ein : tout ; eout where t ::= | m and ::= a |
| 0 | P | , x:a `r]{a} e : m ; e
| 0

| P | `r x.e : a m ; x:a.e

| 0 | P | `r v : ; v

(TA-Lam)

| 0

6 r ftv(0 , P, )

| P, Id , | `r v : ; bId,Id, v x.x

| 1 | P1 | , f :a `r]{a} v : ; v
r0 = r ] {a} ftv(, 1 )
2 = mgu1 (a, ) 2 1 | 3 | P | , f :Gen(, 2 1 , P1 ) `r0 e : m 0 ; e
| 3 2 1 | P | `r letrec f = v in e : m 0 ; letrec f = v in e

(TA-V)

(TA-Rec)

| 1 | P 1 | `r v : 1 ; v
r0 = r ftv(P1 , 1 , 1 )
1 | 2 | P | , x:Gen(, 1 , P1 1 ) `r0 e : m ; e
| 2 1 | P | `r let x = v in e : m ; (x: . e) abs(P1 , v)
e1 6= v
| 1 | P1 | `r e1 : m1 1 ; e1
r0 = r ftv(1 , P1 , m1 1 )
1 | 2 | P2 | , x:1 `r0 e2 : m2 2 ; e2
3 6 r0 ftv(2 , P2 , m2 2 )
| 2 1 | P1 , P2 , (m1 , m2 )  3 | `r let x = e1 in e2 : 3 2 ; bm1 ,m2 ,3 e1 x: . e2

(TA-Do)

| 1 | P1 | `r e1 : m1 1 ; e1
r0 = r ftv(1 , P1 , m1 1 ) 1 | 2 | P2 | `r0 e2 : m2 2 ; e2
a, 3 , 4 , 5 6 r0 ftv(2 , P2 , m2 2 )
3 = mgu2 1 (1 , 2 3 a)
| 3 2 1 | P1 , P2 , (m1 , 4 )  5 , (m2 , 3 )  4 | `r e1 e2 : 5 a ; bm1 ,4 ,5 e1 x: . bm2 ,3 ,4 e2 x
| 1 | P1 | `r e1 : m1 1 ; e1
1 | 2 | P2 | `r0 e2 : m2 ; e2
2 1 | 3 | P3 | `r00 e3 : m3 0 ; e3
4 = mgu3 2 1 ((1 , ), (bool, 0 ))
0 = 4 3 2 1

(TA-Let)

(TA-App)

r0 = r ftv(1 , P1 , m1 )
r00 = r0 ftv(2 , P2 , m2 )
4 , 5 6 r00 ftv(4 , 3 , P3 , m3 )
P = P1 , P2 , P3 , m2 , 4 , m3 , 4 , (m1 , 4 )  5

| 0 | P | `r if e1 then e2 else e3 : 5 ; bm1 ,4 ,5 e1 x: . if x then (bm2 ,Id,4 e2 x.x) else (bm3 ,Id,4 e3 x.x)
v {x, c} ftv(, ) r
dom(00 ) = , internal

(v) =
.0 P
internal = ftv(0 P ) \
.freshr 00 ()
0 . 6= 0 00 () 6= 00 ( 0 )

| 00 0 001 | 00 P | `r v : 00 ; app(v, 00 P )
where

Gen(, , P )
app(e, (P, (m1 , m2 )m3 ))
abs(((m1 , m2 )m, P ), e)

=
=
=

(TA-If)

(TA-XC)

.0 P where = ftv(P ) \ ftv()


app(e, P ) bm1 ,m2 ,m3
app(e, ) = e
bm1 ,m2 ,m : . abs(P, e)
abs(, e)
= e

Figure 6. Algorithmic type inference with evidence translation

set of variables rin (with respect to which we compute fresh type


variables), a term ein can be given the type out in tout if |= out in Pout
is satisfiable. Furthermore, the term is elaborated to out in e, a term
in F.
The rule (TA-Lam) is straightforward: we pick a fresh typevariable a (such that a 6 r) for the formal parameter, type the
body, and propagate the outputs as expected. The rule (TA-V)
corresponds to (TS-V), where we pick a fresh functor variable for
the result. (TA-Rec) binds the recursive variable f to a fresh type
variable a and types its definition v : . We then use mgu1 (a, )
to compute the most-general unifier, 2 , of 1 a and 1 . Finally,
we type the body of the letrec-binding after generalizing the type of
f . (TA-Let) is similar to (TA-Rec), although there is no unification
step. (TA-Do) is also similar, except, as in (TS-Do), there is no
generalization step due to the value restriction, and we compute
a fresh functor variable 3 for the result. In (TA-App), we unify
the type of argument with the formal parameter of the function,
and insert two binds producing a fresh result functor 5 . (TA-If)
requires unifying the types of the branches and the type of the
scrutinee with bool. We insert a morphism in each branch to some
common functor 4 , and then compose this with the functor m1 of
the scrutinee to produce a fresh result functor 5 .
To appreciate (TA-XC), observe first that the constraints
5
generated by all the rules are DAG-like. For example,
O
(TA-App) produces constraints that look like the figure
alongside. If we were to apply the computed unification
m

constraints to the constraint set, we would obtain a dis- 1 4O


torted view of the constraint graph. For example, unim2

fying 5 with 4 would introduce a cycle in the graph,


making it appear as though binds were being composed
cyclically, although no such cyclic composition exists. Of course,
with recursion, the result of a bind may indeed flow into the same
bind again, although, as we will see, this is irrelevant for our analysis.
Now, when generalizing a type using the function Gen(, , P
), as in the syntactic judgment, semantically, we compute the type

.P , where = ftv(P ) \ ftv(). However,


if we were to simply apply the substitution to the constraints, we
lose information about the structure of the constraints and may introduce spurious cycles in the graph-view of the constraints. To
avoid this, we write the type scheme as
.P , maintaining
the unification constraints explicitly within the type scheme. The
semantic interpretation of this type scheme is defined by the translation J
.P K =
.P ; we also write JK for this
interpretation applied pointwise on .
Having understood the structure and intent of our type schemes,
the rule (TA-XC) is essentially standard. At the leaves of every
derivation where we rely on a hypothesis from the environment,
we confirm that the set r includes all the free type-variables in the
input to the judgment. We then look up the binding for v in , and
generate a renaming 00 to replace all the bound variables in the type
scheme as well as any internal variables, internal , with distinct fresh
variables, i.e., 00 is a 1 1 map from , internal to a set of fresh
variables. The internal variables include variables of two kinds:
(1) variables free in P but not in 0 P 0 (i.e., those
that may be substituted away once 0 is applied); and (2) skolem

2013/3/29

constants, i.e., type variables bound in the environment. Freshening


the former kind of variables is necessary to avoid clashes among
multiple instantiations of a type. Semantically, these variables are
innocuous. On the other hand, substituting skolem constants with
fresh variables may appear odd, but, this simplifies our proof of
Lemma 14. Semantically, this is sound since we record a unification
constraint in 00 between the skolems and the fresh variables. In the
conclusion of (TA-XC), we output a substitution 00 0 001 , which,
in effect performs the substitution 0 with respect to the renaming
00 ; the constraints P , also freshened by 00 ; a freshened result type
00 ; and an elaborated term where evidence for the constraints is
passed as an argument.
Despite the additional bookkeeping, the next theorem establishes that our algorithm is semantically identical to OMLs Algorithm W. Since, via Theorem 6, our syntax-directed system is sound
and complete with respect to OMLs syntactic judgment, as a corollary, we have that the algorithm of Figure 6 is sound and complete
with respect to the syntax-directed system in Figure 4.
Theorem 8 (Soundness and completeness). For all | 0 | P | `r
e : t ; e, there exists 0 | 0 JK `W JeK : 0 JtK. Dually, for
all P 0 | JK `W JeK : t0 there exists | 0 | P | `r e : t ; e,
such that 0 P = P 0 and J0 tK = t0 .

Our next theorem establishes that elaborated programs computed by our algorithm are well-typed in F.

Theorem 9 (Well-typed elaborations). If | 0 | P | ` e : t ; e


then {[]} `F 0 (abs(P, e)) : {[0 P t]}.
5.2

Coherence

While our algorithm (and by Theorem 8 OMLs algorithm) computes principal types, as discussed in Section 4, without further
analysis we would have to reject many programs as ambiguous.
OMLs ambiguity restriction rejects any derivation P | `W e : t
where the constraints P contain free variables that are not also free
in or in t. However, for PM, knowing that the semantics of the
inferred constraints is governed by the polymonad laws, we can
do significantly better. In particular, we only deem derivations ambiguous if they run afoul of the following, less restrictive condition.
Definition 10 (Unambiguous derivations). A derivation | 0 | P | `
e : t ; e is unambiguous if and only if for all v V in the
graph-view G = (V, E, A) of 0 P , if in-degree(v) = 0 or
out-degree(v) = 0, then A(v) = M for some constant M, or
A(v) ftv(0 (, t)).
The main result of this section proves that all possible elaborations of an unambiguous derivation produced by our algorithm
have the same semantics, i.e., type inference is coherent (Theorem 15), so long as the program uses a closed, principal relational
polymonad, a common class of polymonads defined as follows.
Definition 11 (Principal relational polymonad). A polymonad
(M, ) is a principal relational polymonad if and only if for any
set F M2 , and any {M1 , M2 } M such that {(m, m0 )M1 |
(m, m0 ) F } and {(m, m0 )  M2 | (m, m0 ) F } ,
then there exists M M such that {M , M1 , M , M2 } ,
and {(m, m0 )  M | (m,Fm0 ) F } . We call M the principal
join of F and write it as F

in our signature, where R and S were not related by a morphism.


As such, these binds do not satisfy the principality condition. However, the existence of these binds imply the existence of two join
functions 1 : a. P (Q a) R a and 2 : a. P (Q a) S a. Thus, by
adding a single additional functor PQ (the product of the two functors) to our functor set, and b:(P, Q)  PQ to our bind set (easily
defined as b = a b.p q. mapP a (Q b) q p) can satisfy the principality condition, since 1 and 2 are morphisms from the principal
join PQ to R and S, respectively. We have yet to find an example
of a useful polymonad that could not easily be transformed into a
principal relational polymonad.
Our other requirement for coherence is closure, defined below.
Definition 12 (Closed polymonad). A polymonad (M, ) is
closed if and only if, for each {(M1 , M2 )M3 , M01 , M1 , M02 ,
M2 , M3 , M03 } , (M01 , M02 )  M03 .
Analyzing closure. Closure for a polymonad is a relatively weak
requirementevery polymonad can easily be closed. Given a polymonad (M, J), such that { : RS T, : T V } J, from
the Diamond and Associativity laws, we require the existence of
1 , 2 , W, 01 , 02 , W 0 such that both diagrams below commute.
RSI
R.1

.I

RW

/ TI


/V

IRS
I.

01 .S

IT

/ W 0S


02

/V

Closure requires that S = W , R = W 0 , for both 1 and 01 to


be identity maps, and for 2 : RS V = 02 : RS V . Given
and , defining 2 : RS V is trivial: we simply set 2 = 02 =
. We call this the covariant composition of a morphism with
a join . Closure also mandates two contravariant compositions of
a morphism and join: and (mapR ) and their analysis is
similar. Transposing this reasoning to the bind-oriented formulation
for PM is straightforward.
An important element of the proof is that our algorithm computes constraints that can be viewed as a DAG; this permits us to
use a well-founded induction principle.
Definition 13 (Acyclic environment). We say that a constraint-bag
P is acyclic if its graph-view is acyclic. An environment is acyclic
if and only if for all x: , = or =
.P and P
is acyclic.
Lemma 14 (Acyclic derivations). For all | 0 | P | `r e : t ;
e, if is acyclic then P is acyclic and ftv(P, t) r = .
Proof. (Sketch) The proof is a simple induction over the structure
of the typing judgment. Roughly, we use the r sets to prove that
the free variables of the constraints generated for each sub-term
of a program are disjoint, and hence combining their constraints
introduces no cycles.
Now we can state and prove coherence.
Theorem 15 (Coherence). For all closed, principal relational
polymonads, and unambiguous, acyclic, derivations in | out | P | `r
e : t ; e; and substitutions = 1 out in and 0 = 2 out in such
that (, t) = 0 (, t) and |= P and |= 0 P ; then e
= 0 e.

Analyzing principality. Informally, in a principal relational polymonad, if there is more than one way to sequence computations in a
pair of functors, there must be a best way to combine them. This
best way is the principal join of the functors, and all other ways to
combine the functors are related to the principal join by morphisms.
Intuitively, it should be clear that this principality requirement is not
particularly onerous. Supposing we had (P, Q)  R and (P, Q)  S

Proof. The two solutions and 0 may only differ on the variables
assigned to nodes with non-zero in- and out-degree in the graphview G = (V, E, A) of P , since (, t) = 0 (, t), and, by assumption, we have an unambiguous derivation. The proof proceeds
by induction on n, the number of these nodes, showing that any

2013/3/29

M7

M8
M5

M6

...

M7

...

...

M5


M3 /M03

. . . A/B . . .

8 O f

M4 /M04

...

...

...J...

...

...

M4 /M04

...

M2 /M02

...

...

?O
6 JO h

...

M6

M1 /M01

M5

M3 /M03

...

m8

M4 /M04


m7

...

M2 /M02

...

. . . A/B . . .

...

0
P2 /P2

M1 /M01

...

...

M3 /M03

......

m6

M2 /M02

...

m4

...

0
P1 /P1

M1 /M01

9 O e

...




m3

m5

m2

...

0
P0 /P0

P
m1

...

M7

...

M6

...

M8

M8

Figure 7. Constraint graphs used to illustrate the proof of coherence (Theorem 15)
syntactic discrepancy between the two solutions is semantically irrelevant. Notice that we carry out our coherence argument independently of the unification constraints out in these are simply treated
as part of the solutions and 0 .
Base case n = 0: Trivial, e is syntactically equal to 0 e.
Induction step: From the induction hypothesis: For all constraintbags P with solutions and 0 differing on at most i variables,
and 0 are coherent.
Topologically sort the graph-view G of P , such that each vertex
m is assigned an index greater than the index of all vertices m0 such
that (m, m0 ) is a directed edge in G. That is, leaf nodes have the
highest indices. Let v be the vertex with the greatest index, such
that A(v) = and () = A and 0 () = B, for A 6= B. Note, v
must have non-zero in and out-degree.
So, P has a sub-graph in the neighborhood of v, call it P , with
shape as shown in left-most graph in Figure 7 Note, since P is
a DAG, m1 may equal m2 etc., although for the purposes of this
argument that is irrelevant.
Now, consider P and 0 P (call them P0 and P00 , shown
together, second from left in Figure 7). Since has the greatest
index of any variable that differs among and 0 , all the immediate
predecessors of (i.e., m5 , . . . , m8 ) have identical assignments in
the two solutions (i.e,. M5 , . . . , M8 ), although the assignments to
m1 . . . m4 may differ, i.e., m1 could be assigned M1 in P0 and M01
in P00 , etc. Now, since we have a principal relational polymonad,
there exists a principal join of {(M5 , M6 ), . . . , (M7 , M8 )}call it
J. So, we can rewrite P0 and P00 to P1 and P10 (second from right
in Figure 7): by the polymonad laws, we have that the composition
of (M5 , M6 )  J and J , A, is the same as the (M5 , M6 )  A
(similarly, for B).
Now, we apply the closure requirement contravariantly in P1 to
b : (M3 , A)  M1 and s : J , A and get that b : (M3 , J) 
M1 must exist in , and from the associativity of binds, we get
that the composition of b and s must be equal to b. Likewise,
the composition of (A, M4 )  M2 and J , A is equivalent to
(J, M4 )  M2 which must also exist in . Similar transformations
are justified for B and the primed versions of the functors in P10 .
Applying these transformations, we get P2 and P20 , the right-most
graph in the figure.
Now, replacing the sub-graph P0 with the equivalent sub-graph
P2 in P ; and replacing P00 with the equivalent sub-graph P20 in
0 P , we obtain two constraints DAGs that differ in at most i nodes.
So, we apply the induction hypothesis and conclude.

S-

G(P, , P 0 ) = (V, E, A)
A(.2)
in-degreeE (.2) = 2
{i, j} = {0, 1} A(.i) = Id
simplify()

P, , P 0

A(.2) 7 A(.j)

S-

G(P, , P 0 ) = (V, E, A)
A(.i) = Id

{i, j} = {0, 1}
A(.j)
out-degreeE (.j) = 1

simplify()

P, , P 0

A(.j) 7 A(.2)
G(P ) = (V, E, A)

V = {.i | A(.i) = }
F = {(A(.0), A(.1)) | v V .{(.0, v), (.1, v)} E}
S-t
F
simplify()

7 F
simplify()

simplify()

simplify()

simplify()

Figure 8. Eliminating open variables in constraints

A simple syntactic transformation on constraints can make inferred types easier to read. For example, the function Hide(P ) below hides duplicate constraints, identity morphisms (which are trivially satisfiable), and constraints that are entailed by the signature.
Hide(P, , P 0 ) = Hide(P, P 0 ) if P, P 0 = m , m |=
Hide(P )
= P
otherwise

Syntactically, given a scheme


.P , we can simply show
the type
.Hide(P ) to the programmer. Formally, however,
the type scheme is unchanged since simply removing constraints
from the type scheme changes our evidence passing elaboration.
More substantially, we can find instantiations for open variables
in a constraint set before generalizing a type (and at the top-level,
before running a program). To do this, we introduce below a modified version of (TS-Let) (from Figure 4); a similar modification is
possible for (TS-Rec).
P 1 | ` v : 1 ; v
, a
= ftv(P1 1 ) \ ftv()
simplify(\
ftv(1 ))
P1
P | , x:Gen(, P1 1 ) ` e : m
P | ` let x = v in e : m
simplify()

Before running a program, we must solve the constraints produced


during type inference, and apply the appropriate evidence for these
constraints in the elaborated program. Additionally, we perform
simplification on constraints prior to generalization to make types
easier to read, but without compromising their utility.

This rule employs the judgment P , defined in Figure 8, to simplify constraints by eliminating some open variables
in P (via the substitution ) before type generalization. There are
three main rules in the judgment (S-), (S-) and (S-t), while the
last two simply take the transitive closure.
The simplification rules operate on the graph-view of a constraint bag. Rule (S-) solves monad variable with monad m for

2013/3/29

6.

Simplification and solving

...

Id

using (S-)

Id

simplify(,)

the situation depicted below.

...

Here, we have a constraint = (Id, m)  , where the only edges


directed inwards to are from Id and m, although there may be
many out-edges from . (The case where = (m, Id)  is
symmetric.) Such a constraint can always be solved without loss of
generality using an identity morphism, which, by the polymonad
laws is guaranteed to exist. Moreover, for a closed polymonad, any
solution to the constraints that chooses = m0 , for some m0 6= m
could also have just as well have chosen = m. Thus, this rule
does not impact solvability of the costraints.
Rule S- follows similar reasoning, but in the reverse direction,
as depicted below.

using (S-)

/ 

simplify(,)

Id

...

...

/! 

Id


Finally, we have the rule (S-t), which exploits the properties of


a principal relational polymonad, as shown below.

/  o

simplify(,)

...

M3

using (S-t)

M4

where

M1

...

M2

...

M1


/Jo

...

M3

M2


M4
F
J = {(M1 , M2 ), . . . , (M3 , M4 )}

In this case, if we have a variable such that all its in-edges are
from pairs of constant functors Mi , then we can simply apply the
join function to compute a solution for . For a closed principal
relational polymonad, if such a solution exists, this simplification
does not impact solvability of the rest of the constraint graph.

Parameterized signatures:
k-ary constructors M
ground constructor M
bind set

bind specifications s
theory constraints

::= | M/k, M
::= M
::= | b:s,
::= .
(M1 , M2 )  M3

theory entailment  : 2P b

Extensions to type system:


types

::=
monadic types
m ::=

a | T | 1 m 2
M|

P 0 . P  ; b;
P |= P 0
Figure 9. PM syntax (extends/modifies Figure 3)
consider nodes in the graph in topological order and, say, apply (St) first, since, if it succeeds, it eliminates a variable. For principal
relational polymonads and acyclic constraint graphs, this process
would always terminate.
However, if unification constraints induce cycles in the constraint graph, simply computing joins as solutions to internal variables may not work. As mentioned in the Introduction, this should
not come as a surprise. In general, finding solutions to arbitrary
polymonadic constraints is undecidable, since, in the limit, they can
be used to encode the correctness of programs with general recursion. Nevertheless, simple heuristics such as unrolling cycles in the
constraint graph a few times may provide good mileage, and such
heuristics are justified by our coherence proof.

7.

Indexed polymonads

Note that our relation is non-deterministic in the way


it picks constraints to analyze, and also in the order in which rules
are applied. In practice, for an acyclic constraint graph, one could

Figure 9 presents PM, which is an extension of PM that supports


k-ary, rather than nullary, polymonad constructors, which we write
M/k when the arity is relevant; they are implemented by (k + 1)ary type constructors. A ground constructor is a constructor M/k
that is applied to k indexes; only ground constructors may be used
in monadic types m (which may appear in normal types or bind
types ). In PM, a bind set pairs binds b to specifications s rather
than single bind types . Specifications s may be polymorphic,
and may include theory constraints . As such, PM defines
extensionally, rather than intensionally as an enumeration. Notice
that the type schemes for PM are unchanged from PM
constraints never appear in , which is thus entirely independent
of the choice of the theory.
We can use this extension to model many useful constructions. For example, to model Wadler and Thiemanns [27] indexed
monad, which represents a type and effect system, we can introduce
a functor W/1, and use W  to represent a computation that produces a -result after exhibiting effects contained within the set .
To specify a polymonadic bind for W , we can use PMs bind specification , , 1 , 2 , 3 .(3 = 1 2 ) (W 1 , W 2 )  W 3 .
Here, we have instantiated the theory to include equality and set
operators like , and the bind specification indicates (informally)
that when composing two computations, the effects are additive.
To interpret the theory constraints, PM requires a theory entailment relation , where elements of this relation are written
. This states that for each i P , there exists
 P ; b;
bi :.
(M1 , M2 )  M3 in and a substitution 0 such that
= 0 (M1 , M2 )  M3 , and the constraints 0 are satisfiable.
Here, is a substitution for the free (non-constant) variables in ,
while 0 is an instantiation of the abstracted variables in the bind
specification. We write  P ; when we do not care about the
elaborated binds. Obviously we require the entailment relation to

10

2013/3/29

Example. Recall the information flow example we gave in Section 3.2. Its principal type (given below) is hardly readable:

i .P0 intref H intref L 26 ()


whereP0 = (Id, 18 )  17 , (20 , IST H L)  24 , (2 , 6 )  26 ,
(Id, Id)  9 , (Id, 9 )  8 ,
(Id, Id)  15 , (17 , 21 )  20 ,
(8 , 4 )  6 , (Id, Id)  21 ,
(Id, 15 )  14 , (Id, Id)  18 ,
(Id, 3 )  2 , (Id, 12 )  11 ,
(14 , 24 )  23 , (Id, IST H H)  12 ,
(Id, IST H L)  3 , (11 , 23 )  25 ,
(Id, Id)  4 , (25 , Id)  4 .

However, after applying (S-) and (S-) several times, and then
hiding redundant constraints, we simplify P0 to (IST H L, 6 ) 
26 , (Id, Id)6 , (IST H H, Id)6 . Then, applying (S-t) to 6
we get (IST H L, IST H H)  26 , which cannot be simplified
further, since 26 appears in the result type.
Pleasingly, this process yields a simpler type that can be used
in the same contexts as the original principal type, so we are not
compromising the generality of the code by simplifying its type.
Lemma 16 (Simplification improves types). For a closed, principal relational polymonad, given and 0 where is
.P
and 0 is an improvement of , having form
0 .P where
simplify()

P and 0 = dom(). Then for all P 00 , , x, e, m, ,


if P 00 | , x: ` e : m such that |= P 00 then there exists some P 000
such that P 000 | , x: 0 ` e : m and |= P 000 .
simplify()

define a polymonad, i.e.,  is admissible if and only if the (possibly


infinite) set {b: |  ; b; } satisfies the polymonad laws.
The entailment rule for PM from Figure 4 is replaced with the
one in Figure 9. The only difference is that a constraint P 0 is
also provable if is entailed (without substitution) by the theory
directly. Simplification applies to PM just as for PM.
Now we present several examples in this extended system.

matches the seconds precondition.

IST for an arbitrary lattice We can generalize our IST example


from Section 3.1 to support an arbitrary lattice of security labels,
with H and L as the topmost and bottommost elements in the lattice, respectively. First, we define the type of integer references to
be intref l, where we extend the language of types to include lattice elements l as security labels. Second, we define a binary functor IST p l. In PM we had to define a family of constructors by
inlining the labels in the constructors, but now we can properly
parameterize the constructor with labels. Our reference functions
read and write are now polymorphic in the security label:
read : l. intref l IST H l int
write : l. intref l int IST l L ()
Finally, we define a lattice theory and define the bind set to
permit only legal information flows (cf. Section 3.1):

We use the type index send to denote a protocol state that requires a message of type to be sent, and then transitions to . Similarly, the type index recv denotes the protocol state in which
once a message of type is received, the protocol transitions to .
We also use the index end to denote the protocol end state. The
signatures of two primitive operations for sending and receiving
messages captures this behavior.

=
::=
=

=
=

Id, A/2
bId : (Id, Id)  Id,
mapA : , . (A , Id)  A ,
appA : , . (Id, A )  A ,
unitA : . (Id, Id)  A ,
bindA : . (A , A )  A

send : .
A (send ) ()
recv : . ()
A (recv )
Using these definitions, consider the following PM program that
implements one side of a simple protocol that sends a message x,
waits for an integer reply y, and returns y+1.
let go = x. let = send x in incr (recv ())

IST/2
l1 v l2 | 1 , 2
bId : Id , Id,
unitIST : p, l.Id , IST p l,
mapIST : p, l, p0 , l0 .p0 v p, l v l0
IST p l , IST p0 l0 ,
appIST : p1 , l1 , p2 , l2 .p2 v p1 , l1 v l2
(Id, IST p1 l1 )  IST p2 l2 ,
bIST : p1 , l1 , p2 , l2 , p3 , l3 .
l1 v p 2 , l 1 v l3 , l 2 v l3 , p 3 v p 1 , p 3 v p 2
(IST p1 l1 , IST p2 l2 )  IST p3 l3

Type inference produces the following simplified type


, , , 10 .
(A (send ) ), (A (recv int) ))  10
( 10 int)

Note that the theory constraints for session types are straightforward, and can be solved by unification when instantiating the final
program (e.g., to call go 0).

To interpret the theory constraints, we instantiate  to a standard


theory of lattice contraints. The implementations of the binds and
read and write functions are as in Section 3.1. With these definitions
in hand, we infer the following (simplified) type for our add interest
program where occurrences of readL etc. are replaced with their
generic counterparts:
l11 , l4 , 6 , 27 . P
intref l11 intref l4 27 ()
where P = (IST H l11 , IST l11 L)  6 , (IST H l4 , 6 ) 
27 , (Id, Id)  6 . Comparing this type to the simplified type presented in the previous section, we can see that constraints are
more complex, but that the type is also more generic; e.g., the
two input references are polymorphic in their security label. Consider applying add interest secret ac public intr where secret ac :
intref H and public intr : intref L. To do this, we must prove
for some whose domain is 6 and 27 . Here,
 Pinst ; b;
Pinst = [H /lll , L/l4 ]P ; i.e., the constraints instantiated with the
labels of secret ac and public intr. And indeed we can easily satisfy
these constraints by choosing 6 = 27 = IST H H .

Contextual type and effect systems We have already sketched an


encoding of Wadler and Thiemanns [27] type and effect systems
as a polymonad. As our final example, we show that a recent type
and effect system for contextual effects [19] (which subsumes traditional type and effects) is a polymonad. We define a polymonad
CE  , for the type of a computation which itself has effect  and produces a value of type , and appears in a context in
which the prior computations have effect and whose subsequent
computations have effect .
Our encoding begins by describing a language of type indices
to describe effect sets. Indices are sets of atomic effects A1 . . . An ,
with the empty effect, > the effect set that includes all other
effects, and the union of two effects. We also introduce theory
constraints for subset relations and extensional equality on sets,
with the obvious interpretation.
types
theory constraints

::=
::=

| A1 . . . An | | > | 1 2
0 | = 0 | ,

Parameterized monads, and session types PM can also express


Atkeys parameterized monad [2], which has been used to encode
disciplines like regions [13] and session types [20]. The type constructor A p q can be thought of (informally) as the type of a
computation producing a -typed result, with a pre-condition p and
a post-condition q. As a concrete example, consider Pucella and
Tovs encoding of session types [20]. The type A represents
a computation involved in a two-party session which starts in protocol state and completes in state , returning a value of type
. The key element of the signature is the bindA, which permits composing two computations where the firsts post-condition

The following binds capture the tracking of contextual effects:


M = Id, CE /3

= bId : (Id, Id)  Id,


unitce : (Id, Id)  CE > >
bindceId : 1 , 2 , 1 , 2 , 1 , 2 .
(2 1 , 1 2 , 2 1 )
(Id, CE 1 1 1 )  CE 2 2 2
bindce : 1 , 1 , 1 , 2 2 , 2 , 3 .
(2 2 = 1 , 1 1 = 2 , 1 2 = 3 )
(CE 1 1 1 , CE 2 2 2 )  CE 1 3 2
The bind unitce lifts a computation into a contextual effect
monad with empty effect and any prior or future effects. The bind
bindceId expresses that it is safe to consider an additional effect for
the current computation (the s are covariant), and fewer effects for
the prior and future computations (s and s are contravariant).

11

2013/3/29

Finally, bindce composes two computations such that the future effect of the first computation includes the effect of the second one,
provided that the prior effect of the second computation includes
the first computation; the effect of the composition includes both
effects, while the prior effect is the same as before the first computation, and the future effect is the same as after the second computation.

cursion have the same semantics. Tate also does not address type
inference.

References
[1] M. Abadi, A. Banerjee, N. Heintze, and J. Riecke. A core calculus of
dependency. In POPL, volume 26, pages 147160, 1999.
[2] R. Atkey. Parameterised notions of computation. J. Funct. Program.,
19(3-4):335376, 2009.

8.

Related work

A variety of past work has aimed to refine the conventional notion of monads. Several examples, including Atkeys parameterized
monads [2], Wadler and Thiemanns indexed monads [27], and applications thereof, were cited in the introduction and given in Section 7. Each of these constructions can be viewed as an instance of
a polymonad. Filliatre [8] proposed generalized monads as a means
to more carefully reason about effects in a monadic style, and his
work bears a close resemblance to Wadler and Thiemanns. Generalized monads can also be seen as instances of polymonadsit is
easy to show that the polymonad laws imply Filliatres six required
identities. Conversely, it is clear that some useful examples cannot
be expressed using any of these prior refinements to monads; for
example, our IST polymonad cannot be expressed due to its exclusion of certain (information-flow-violating) compositions. Thus
polymonads provide greater expressive power.
Kmetts Control.Monad.Parameterized Haskell package [14]
provides a typeclass for ternary bind-like operators that have a signature resembling ours (m1 , m2 )  m3 . One key limitation is that
Kmetts binds must be functionally dependent; i.e., m3 must be
a function of m1 and m2 . As such, it is not possible to program
morphisms between different monadic constructors, i.e., the pair of
binds (m1 , Id)  m2 and (m1 , Id)  m3 would be forbidden, so
there would be no way to convert from m1 to m2 and from m1 to
m3 in the same program. Kmett also does not permit polymorphic
unitshe requires units into Id, which may later be lifted. But this
only works for first-order code before running afoul of Haskells
ambiguity restriction. Polymonads do not have either limitation.
Kmett does not discuss laws that should govern the proper use of
non-uniform binds.
Another line of past work has focused on making monadic
programming easier. Haskells do notation exposes the structure
of a monadic computation, and typeclass inference can determine
which binds and units should be used, but the placement of morphisms is left to the programmer. The problem is that the use of
morphisms (e.g., if defined as a typeclass) would frequently lead
to open type variables, which Haskells typeclass inference deems
ambiguous. Inference with Kmetts class has the same problems.
For ML, we have already discussed our own prior work [24] in detail throughout the paper.
Concurrently with our work Tate developed a semantic framework called productors for describing the sequential composition
of effects [25]. Polymonads are closely related to a special class
of productors called productoids, although subtle differences in our
formulations mean that neither subsumes the other precisely. Productors allow a more general class of non-compositional effect system than productoids, and some of those systems are not polymonads either. However, this does not seem to provide any additional
expressive power since as Tate conjectures (in private communication), any productor can be encoded as a productoid by adding
more functors and joins. In all examples we have seen, these can be
encoded as polymonads as well. Tate proves a coherence result in a
first-order imperative setting establishing that sequential compositions of productor joins are fully associative. Our coherence result
is different in that it proves that all well-typed elaborations of a
higher-order program with branching, function application, and re-

12

[3] G. Cooper and S. Krishnamurthi. Embedding dynamic dataflow in a


call-by-value language. In ESOP, 2006.
[4] K. Crary, A. Kliger, and F. Pfenning. A monadic analysis of
information flow security with mutable state. Journal of functional
programming, 15(02):249291, 2005.
[5] N. Danielsson. Lightweight semiformal time complexity analysis for
purely functional data structures. In POPL, pages 133144, 2008.
[6] D. Devriese and F. Piessens. Information flow enforcement in monadic
libraries. In TLDI, pages 5972, 2011.
[7] C. Elliott and P. Hudak. Functional reactive animation. In ICFP,
pages 263273, 1997.
[8] J.-C. Filliatre. A theory of monads parameterized by effects, 1999.
[9] J.-Y. Girard. Interpretation fonctionelle et e limination des coupures
de larithmetique dordre superieur. PhD thesis, Universite Paris VI
I, 1972.
[10] J. Goguen and J. Meseguer. Security policy and security models. In
Symposium on Security and Privacy, pages 1120, 1982.
[11] G. Hutton and E. Meijer. Monadic Parsing in Haskell. JFP, 8(4),
1998.
[12] M. P. Jones. A theory of qualified types. In ESOP, 1992.
[13] O. Kiselyov and C. Shan. Lightweight monadic regions. In ACM
SIGPLAN Notices, volume 44, pages 112. ACM, 2008.
[14] E. Kmett. Control.Monad.Parameterized package. http:
//hackage.haskell.org/packages/archive/monad-param/
0.0.4/doc/html/Control-Monad-Parameterized.html, 2012.
[15] S. M. Lane. Categories for the working mathematician. SpringerVerlag, 1971.
[16] P. Li and S. Zdancewic. Encoding information flow in Haskell. In
CSFW, pages 1627, 2006.
[17] E. Moggi. Computational lambda-calculus and monads. In LICS,
1989.
[18] A. Nanevski, G. Morrisett, and L. Birkedal. Polymorphism and
separation in hoare type theory. In In icfp, pages 6273. ACM Press,
2006.
[19] I. Neamtiu, M. Hicks, J. S. Foster, and P. Pratikakis. Contextual effects
for version-consistent dynamic software updating and safe concurrent
programming. In POPL, 2008.
[20] R. Pucella and J. Tov. Haskell session types with (almost) no class. In
ACM SIGPLAN Notices, volume 44, pages 2536. ACM, 2008.
[21] N. Ramsey and A. Pfeffer. Stochastic lambda calculus and monads of
probability distributions. In POPL, pages 154165, 2002.
[22] A. Russo, K. Claessen, and J. Hughes. A library for light-weight
information-flow security in haskell. In Haskell, 2008.
[23] A. Sabelfeld and A. Myers. Language-based information-flow
security. J. SAC, 21(1), 2003.
[24] N. Swamy, N. Guts, D. Leijen, and M. Hicks. Lightweight monadic
programming in ML. In ICFP, 2011.
[25] R. Tate. The sequential semantics of producer effect systems. In
POPL, 2013.
[26] P. Wadler. The essence of functional programming. In POPL, 1992.
[27] P. Wadler and P. Thiemann. The marriage of effects and monads.
ACM Trans. Comput. Logic, 4:132, January 2003.

2013/3/29

You might also like