Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
Abstract<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012<br />
Dominic Orchard<br />
Computer Laboratory, University of Cambridge, UK<br />
dominic.orchard@cl.cam.ac.uk<br />
The category-theoretic notion of a monad fortuitously matches the<br />
underlying structure of various notions of effectful computation<br />
thus it can be used for abstraction in both programming and semantics.<br />
The utility and ubiquity of monads is such that some languages<br />
provide <strong>notation</strong> to simplify programming with monads. Comonads,<br />
the dual of monads, have been similarly applied in semantics<br />
and programming but remain relatively under-utilised compared<br />
with monads. There are several useful examples of comonads but<br />
a lack of <strong>notation</strong> prevents wider adoption and indeed further understanding<br />
of comonads as an abstraction mechanism. We propose<br />
a lightweight syntax for programming with comonads in Haskell,<br />
analogous to the do-<strong>notation</strong> for monads, accompanied by examples<br />
of comonads in the <strong>notation</strong>. The utility of comonads compared<br />
to monads is analysed and we present the case that, whilst<br />
less diverse than monads, comonads are useful in programming.<br />
1. Introduction<br />
Concepts from category theory have proven extremely effective as<br />
tools for abstraction in both language semantics and programming,<br />
simplifying definitions and reasoning. A well known example is the<br />
monad structure which Moggi applied to structure the semantics<br />
of languages with computational effects such as non-determinism,<br />
partiality, and state [8, 9]. Following the use of monads in semantics,<br />
Wadler showed that monads can be used directly in programming<br />
as a design pattern for organising programs and encapsulating<br />
impure features in a pure language [20, 21]. Monads are so<br />
effective as an abstraction technique that some languages provide a<br />
lightweight syntax simplifying programming with monads, such as<br />
the do-<strong>notation</strong> in Haskell and the let! <strong>notation</strong> in F# [13].<br />
In the functional programming interpretation, a monad comprises<br />
a parametric data type M and operations which provide<br />
well-behaved composition to functions of type a → Mb. Thus,<br />
monads provide abstraction for the composition of functions with<br />
structured output. An example output structure encodes partiality,<br />
or single exceptions, with the parametric type Ma = a + 1.<br />
Comonads are the dual structure to monads providing abstraction<br />
over composition of functions with structured input i.e. functions<br />
of type Ca → b where C is a data type providing structure<br />
to a function’s inputs. An example input structure encodes global<br />
implicit parameters with the parametric type Ca = a × x, where x<br />
is the type of some global parameter.<br />
[Copyright notice will appear here once ’preprint’ option is removed.]<br />
A Notation for Comonads<br />
Alan Mycroft<br />
Computer Laboratory, University of Cambridge, UK<br />
am@cl.cam.ac.uk<br />
There are various examples of comonads in functional programming<br />
in the literature such as dataflow programming with<br />
streams [18], attribute evaluation [16], array computations [11],<br />
context-dependent computation [17], and more [6]. However,<br />
comonads have been less widely accepted than monads.<br />
The primary reason for the underuse of comonads in programming<br />
is that there are fewer known useful examples, or at least less<br />
diverse examples, compared to monads. For the examples that are<br />
known, there is no language support to simplify programming with<br />
these comonads. We conjecture that the lack of <strong>notation</strong> further impedes<br />
use of comonads and fresh experimentation with comonads<br />
as a design pattern for programming.<br />
Presently, we propose a <strong>notation</strong> for programming with comonads<br />
along with various practical examples written in the <strong>notation</strong>.<br />
The specific contributions are:<br />
• a lightweight <strong>notation</strong> for programming with comonads in<br />
Haskell, called the <strong>codo</strong>-<strong>notation</strong>, which considerably simplifies<br />
comonadic programming (Section 2)<br />
• a simple desugaring of <strong>codo</strong>-<strong>notation</strong> into the operations of a<br />
comonad (Section 3) and an accompanying equational theory<br />
for the syntax (Section 4);<br />
• various practical examples of comonads using the <strong>notation</strong>, including<br />
lists (Section 2.1), arrays (Section 2.2), dataflow/streams<br />
(Section 5.2), dynamic programming (Section 5.3), numerical<br />
functions (Section 5.1), and implicit parameters (Section 6.2);<br />
• discussion of the design of the <strong>codo</strong>-<strong>notation</strong> from the perspective<br />
of language semantics and comparison to the analogous do<strong>notation</strong><br />
for monads (Section 6);<br />
• discussion of extensions to the <strong>codo</strong>-<strong>notation</strong> (Section 8), and<br />
comparison of <strong>codo</strong>-<strong>notation</strong> to Haskell’s rebindable syntax<br />
and arrow <strong>notation</strong> (Section 7).<br />
The <strong>codo</strong>-<strong>notation</strong> is provided using macros in GHC as a library<br />
available at: http://github.com/dorchard/<strong>codo</strong>-<strong>notation</strong>.<br />
2. Comonads and <strong>codo</strong>-<strong>notation</strong><br />
Comonads and <strong>codo</strong>-<strong>notation</strong> will be introduced concurrently in<br />
the following two sections using two example comonads.<br />
Section 2.1 provides an intuition for comonads in terms of composition<br />
for functions with structured input and an introduction to<br />
the basic binding structure of the <strong>codo</strong>-<strong>notation</strong>, using a comonad<br />
of non-empty lists as an example. Section 2.2 provides an intuition<br />
for comonads in terms of contextual computations and shows the<br />
use of tuple patterns in <strong>codo</strong>-<strong>notation</strong> and the simplifications provided<br />
when working with multi-parameter comonadic operations.<br />
Array comonads will be used as examples.<br />
2.1 Composing computations with structured input<br />
In Haskell a comonad can be defined by a parametric data type and<br />
an instance of the following type class:<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 1 2012/9/22
class Comonad c where<br />
current :: c a → a<br />
(≪=) :: (c a → b) → c a → c b<br />
where current and ≪= are total functions and satisfy a number<br />
of additional laws (Section 4). We pronounce the operator ≪= as<br />
cobind. The names of the comonad operations vary in the literature<br />
and existing usage.<br />
The types of the operations are suggestive of their behaviour<br />
which is further prescribed by the comonad laws. One intuition for<br />
comonads, is of an abstraction for composing functions with structured<br />
input, that is, for functions with input type of a parametric<br />
data type common to each function.<br />
Comonads for functions with structured input A simple intuition<br />
for current is a prosaic function with structured input, where<br />
the additional structure on the input is discarded and a particular<br />
element extracted. The basic intuition for cobind is that a function<br />
with structured input has its input structure propagated to the result.<br />
An example input structure, for which comonads can provide a<br />
well-behaved composition, is non-empty lists. The current operation<br />
returns the head of the list, hence the non-empty restriction so<br />
that current is total. The cobind operation is akin to map, except<br />
that the parameter function is applied to successive suffixes of the<br />
parameter list, rather than just single elements. For example:<br />
sum ≪= [1, 2, 3, 4] ≡ [10, 9, 7, 4]<br />
current ≪= [1, 2, 3, 4] ≡ [1, 2, 3, 4]<br />
id ≪= [1, 2, 3, 4] ≡ [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]<br />
where sum :: Num a ⇒ [a ] → a.<br />
The second example is an instance of one of the comonad laws,<br />
discussed in Section 4. The third example shows the suffixes that<br />
are passed to the parameter function by cobind.<br />
The Comonad instance for (non-empty) lists is defined:<br />
instance Comonad [ ] where<br />
current (x : xs) = x<br />
f ≪= [x ] = [f [x ]]<br />
f ≪= x = (f x) : (f ≪= (tail x))<br />
Comonads for composition Given a function f : c x → y and<br />
function g : c y → z the composite, which we’ll write as g ˆ◦ f, is<br />
defined by first applying cobind to f , thus (f ≪=) : c x → c y, and<br />
then composing g using the usual composition operator:<br />
g ˆ◦ f = g ◦ (f ≪=) : c x → z<br />
Thus cobind provides the basis of composition. The comonad laws<br />
follow from the requirement that this composition is associative<br />
and has a left and right identity (Section 4).<br />
Codo-<strong>notation</strong> abstracts composition The <strong>codo</strong>-<strong>notation</strong> provides<br />
a let-binding-like syntax abstracting the use of cobind to<br />
compose comonadic operations (operations with structured input).<br />
The <strong>codo</strong>-<strong>notation</strong> is polymorphic in the underlying comonad, using<br />
Haskell type classes for overloading.<br />
For non-empty lists, an example composite operation computes<br />
the averages of list suffixes (where fromIntegral is a cast from<br />
integers to fractional numbers):<br />
ave x = let s = sum ≪= x<br />
l = length ≪= x<br />
in (current s) / (fromIntegral $ current l)<br />
This example can be written equivalently in <strong>codo</strong>-<strong>notation</strong>:<br />
ave = <strong>codo</strong> x ⇒ s ← sum x<br />
l ← length x<br />
(current s) / (fromIntegral $ current l)<br />
Applying ave to a list using cobind returns the suffix-list averages:<br />
ave ≪= [1, 2, 3, 4] ≡ [2.5, 3.0, 3.5, 4.0]<br />
Alternatively, ave can be applied directly to a list, returning the<br />
average for that list, not for suffixes, e.g. ave [1, 2, 3, 4] = 2.5.<br />
As seen in this example, the <strong>codo</strong>-<strong>notation</strong> primarily provides<br />
an abstraction over cobind. A <strong>codo</strong>-block is parameterised, where<br />
a pattern match between the <strong>codo</strong> keyword and double arrow ⇒<br />
provides a binding site for the parameter. In the case of ave, the<br />
pattern is a simple variable pattern.<br />
For a variable pattern parameter, a <strong>codo</strong>-block is typed by the rule:<br />
[varP]<br />
Γ, x : c t ⊢c e : t ′<br />
Γ ⊢ (<strong>codo</strong> x ⇒ e) : Comonad c ⇒ c t → t ′<br />
where ⊢c types the binders of the <strong>codo</strong>-block. Since comonads<br />
capture functions with structured input the parameter of a <strong>codo</strong>block<br />
is essential as it provides the input structure.<br />
A <strong>codo</strong>-block has zero or more binding statements, of the form<br />
p ← e, preceding a final result expression. A binding inside a<br />
<strong>codo</strong>-block to a variable pattern is typed by the rule:<br />
[varB]<br />
Γ ⊢c e1 : t Γ, y : c t ⊢c e2 : t′<br />
Γ ⊢c y ← e1; e2 : t ′<br />
Variables bound within the <strong>codo</strong>-block (the local variables) are the<br />
inputs of further expressions in the block. The presence of the toplevel<br />
parameter means that every expression in the block has at<br />
least one input. The interpretation of binding an expression is thus<br />
composition of this expression as a function from its inputs with the<br />
interpretation of the rest of the block. Thus in [varB] the binding of<br />
e1 : t to y implies y : c t in the rest of the block.<br />
The binding behaviour within a <strong>codo</strong>-block can be illustrated<br />
when ave is applied to a list [1, 2, 3, 4] by showing the intermediate<br />
values of bindings:<br />
(<strong>codo</strong> x ⇒ y ← sum x -- x = [1, 2, 3, 4], sum x = 10<br />
z ← length x -- y = [10, 9, 7, 4], length x = 4<br />
... -- z = [4, 3, 2, 1]<br />
) [1, 2, 3, 4]<br />
The typing rules for <strong>codo</strong>-<strong>notation</strong> are collected in Figure 2.2.<br />
Comonads are functors, cobind generalises map Haskell’s Functor<br />
type class provides an operation for transforming the elements of a<br />
parametric data type, of which map is the instance for lists:<br />
class Functor f where fmap :: (a → b) → f a → f b<br />
instance Functor [ ] where fmap = map<br />
All comonads are functors where fmap can be defined in terms of<br />
cobind and current:<br />
cmap :: Comonad c ⇒ (a → b) → c a → c b<br />
cmap f x = (f ◦ current) ≪= x<br />
While fmap applies its parameter function to a single element,<br />
cobind applies its parameter function to possibly the whole parameter<br />
structure or a particular substructure, such as the suffix lists of<br />
the non-empty list comonad. Thus cobind generalises fmap.<br />
2.2 Contextual computations & multi-parameter operations<br />
Much of the existing literature on comonads in programming describes<br />
comonads in general as capturing notions of context dependence<br />
(for example [17]). This contextual understanding provides<br />
a useful intuition for comonads further than that provided by the<br />
understanding of composition for functions with structured input.<br />
In the contextual interpretation, a comonadic value of type c a is<br />
a value of type a which is dependent on some notion of context or is<br />
contextually situated i.e. it exists relative to some other information<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 2 2012/9/22
in the context of a computation. The current operation extracts a<br />
non-context-dependent value, by evaluating a value c a at a known<br />
context, perhaps a default context or some current context inside<br />
the comonad. The cobind operation takes a function c a → b<br />
from context-dependent values c a to non-context-dependent values<br />
b and returns a function c a → c b which propagates the<br />
context-dependence of the parameter c a by applying the parameter<br />
function at all possible contexts to build a context-dependent<br />
c b value. The cmap operation corresponds to the application of a<br />
non-contextual function over a comonadic structure.<br />
The contextual (or context-dependent) understanding of comonads<br />
can be contrasted with the understanding of monadic values<br />
m a as effectful (or effect-producing) computations of a value of<br />
type a. The effectful understanding provides intuition about the use<br />
of monads further than the intuition of monads as just an abstraction<br />
for composition of functions with structured output a → m b.<br />
The following comonad of arrays fits the contextual understanding<br />
particularly well.<br />
Example: modelling discrete, finite environments Many applications,<br />
particular in scientific computing and graphics-oriented applications,<br />
employ a computational pattern known as the stencil (or<br />
structured grid) pattern. The stencil pattern is characterised by iteration<br />
over the index space of an array, or collection of arrays,<br />
which represent some discretised environments. An array can be<br />
understood as defining a value which is dependent upon its position<br />
in the array. Thus arrays are context-dependent values where<br />
the context is the position in the array.<br />
Cellular automata, such as Conway’s Game of Life, apply<br />
the stencil pattern of computation, which Piponi pointed out is<br />
comonadic [14]. We have previously used comonads as the basis<br />
for a domain-specific language of stencil computations on arrays<br />
[11], and Capobianco and Uustalu used comonads for reasoning<br />
about cellular automata [4]. The examples here use operations<br />
from image processing (convolutions) on one-dimensional arrays.<br />
Cursored arrays Stencil computations can be captured succinctly<br />
by the cursored (or pointed) array comonad. Its data type comprises<br />
an array paired with an index called the cursor:<br />
data PArray i a = PA (Array i a) i<br />
where Array i a is the built-in array data type in Haskell, with<br />
index type i and element type a. Thus a cursored array PArray i a<br />
is a value a dependent upon a context of type i (its position within<br />
the array) paired with a current context (the cursor).<br />
The operations of the comonad are defined:<br />
instance Ix i ⇒ Comonad (PArray i) where<br />
current (PA a c) = a ! c<br />
f ≪= (PA a c) =<br />
let es ′ = map (λi → (i, f (PA a i))) (indices a)<br />
in PA (array (bounds a) es ′ ) c<br />
Thus current extracts the value at the current context (where ! is the<br />
array indexing operation); cobind applies the parameter operation<br />
f for every index of the array, passing the parameter array to f<br />
with the cursor set to each index. The results of applying f to the<br />
parameter array at each index is collected into a index-value pair<br />
list from which is the result array is constructed.<br />
To illustrate the definition of cobind, consider the following<br />
helper function which returns the current context of an array:<br />
context :: PArray i a → i<br />
context (PA a c) = c<br />
Applying context to an array using cobind returns the array where<br />
the value of each element is its index e.g.<br />
context≪= a0 a1 a2 . . . @c = 0 1 2 . . . @c<br />
where @c denotes the cursor of the array.<br />
The cursor of the returned array has the same cursor as the<br />
parameter array, which allows array operations to be nested.<br />
A useful example operation from image processing is the onedimensional<br />
discrete Laplace transform:<br />
laplace1D :: Fractional a ⇒ PArray Int a → a<br />
laplace1D (PA a i) =<br />
let (b1 , b2 ) = bounds a<br />
in if (i > b1 ∧ i < b2 )<br />
then a ! (i − 1) − 2 ∗ (a ! i) + a ! (i + 1)<br />
else 0.0<br />
In the contextual understanding, laplace1D computes a value for<br />
the current context i from a local neighbourhood of values at<br />
relative contexts i, i − 1, and i + 1. The operation can applied<br />
over the entire index space of an array by cobind. A bounds check<br />
is performed, since indices i − 1 and i + 1 will be out-of-bounds<br />
at the edge of the array, thus a default value of 0.0 is returned for<br />
the edge indices. Alternate comonadic definitions can be used to<br />
abstract the bounds checking process [10].<br />
Codo and context An example composite operation of the Laplace<br />
operator followed by a local mean operation (which at each index<br />
i computes the mean of the values at i, i − 1 and i + 1) is:<br />
prog1 = <strong>codo</strong> x ⇒ y ← laplace1D x<br />
z ← localMean1D y<br />
current z<br />
Within prog1 the context of the computation is fixed at the context<br />
of the incoming parameter x. Thus, if context x = c then<br />
context y = c = context z . Therefore current z extracts a value<br />
from z at c. The preservation of the context throughout a <strong>codo</strong>block<br />
is a consequence of a property we call shape preservation of<br />
comonads which will be discussed further in Section 4.1.<br />
Multi-parameter operations Multi-parameter comonadic functions<br />
are significantly more difficult to compose without <strong>codo</strong>.<br />
An example operation is the pointwise addition of two comonad<br />
values, which is polymorphic in the comonad since no comonadspecific<br />
operations are used:<br />
plus :: (Comonad c, Num a) ⇒ c a → c a → a<br />
plus x y = current x + current y<br />
Multi-parameter comonadic functions can be composed with other<br />
operations naturally using <strong>codo</strong>-<strong>notation</strong> e.g.<br />
prog2 = <strong>codo</strong> a ⇒ b ← localMean1D a<br />
c ← laplace1D b<br />
d ← plus b c<br />
localMean1D d<br />
The definition of plus can be inlined but is separate here for brevity.<br />
Without <strong>codo</strong>, prog2 is considerably more difficult to write<br />
since plus must be applied correctly to both parameters such that it<br />
is applied pointwise. An equivalent program without <strong>codo</strong> is:<br />
prog2 a = let b = localMean1D ≪= a<br />
d = (λb ′ → let c = laplace1D ≪= b ′<br />
in plus b ′ c) ≪= b<br />
in localMean1D d<br />
The plus operation is nested within a function applied to b using<br />
cobind, and which has a nested use of cobind. The definition of<br />
c is laplace1D applied to b ′ using cobind thus b ′ and c have the<br />
same cursor and plus b ′ c is the pointwise addition.<br />
The following is an incorrect use of plus for pointwise addition:<br />
prog2bad a = let b = localMean1D ≪=a<br />
c = laplace1D ≪=b<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 3 2012/9/22
d = (plus b) ≪=c<br />
in localMean1D d<br />
The first parameter b of plus has the same cursor as a whilst the<br />
second parameter c, applied by cobind, has a varying cursor. Thus<br />
d is not the pointwise addition of b and c, rather the addition of the<br />
value at the (fixed) current context of b to each element in c.<br />
The <strong>codo</strong>-<strong>notation</strong> therefore considerably simplifies the use of<br />
multi-parameter operations, eliminating possible bugs from unsynchronised<br />
cursors. If the unusual behaviour of prog2bad is intended<br />
it can be encoded in the <strong>codo</strong>-<strong>notation</strong> using nested <strong>codo</strong>-blocks:<br />
prog2 ′ = <strong>codo</strong> a ⇒ b ← localMean1Db a<br />
(<strong>codo</strong> b ′ ⇒ c ← laplace1D b ′<br />
d ← plus b c<br />
localMean1D d) b<br />
The plus operation is applied to b (not b ′ ) and c, where b is bound<br />
in the outer <strong>codo</strong> and thus has its cursor fixed, whilst c is bound in<br />
the inner <strong>codo</strong> and thus has its cursor varying. Thus, referencing<br />
a comonadic variable bound outside of a <strong>codo</strong> block (even if it<br />
is bound within an outer <strong>codo</strong>-block) implies that the variable is<br />
unsynchronised with respect to those bound within the block.<br />
Comonads of tuples The <strong>codo</strong>-<strong>notation</strong> can take multiple parameters<br />
using a tuple pattern in the parameter to <strong>codo</strong>. For example,<br />
the following program has tuple pattern (x, y) and computes the<br />
pointwise addition of the Laplace transform of x and y:<br />
prog3 :: PArray Int (Double, Double) → Double<br />
prog3 = <strong>codo</strong> (x, y) ⇒ a ← laplace1D x<br />
b ← laplace1D y<br />
(current a) + (current b)<br />
The type of prog3 shows that, instead of two parameters, it takes<br />
a single comonadic parameter with tuple elements, of the form<br />
c (a, b). However, inside the block x : c a and y : c b thus the<br />
desugaring of <strong>codo</strong> unzips the parameter (Section 3). The intention<br />
is that x and y are synchronised in their cursors. The corresponding<br />
typing rule for tuples patterns is [tupP] in Figure 2.2.<br />
To apply prog3 to a pair of arrays its parameters must first be<br />
zipped which is provided by the czip operation of ComonadZip:<br />
class Comonad c ⇒ ComonadZip c where<br />
czip :: (c a, c b) → c (a, b)<br />
For PArray, czip can be defined:<br />
instance (Eq i, Ix i) ⇒ ComonadZip (PArray i) where<br />
czip (PA a c, PA a ′ c ′ ) =<br />
if (c �≡ c ′ ) then error "Cursors must be equal"<br />
else let es ′′ = map (λi → (i, (a ! i, a ′ ! i))) (indices a)<br />
in PA (array (bounds a) es ′′ ) c<br />
Thus only arrays of the same shape and cursor can be zipped together.<br />
For other comonads it is easier to relax this condition. In<br />
the contextual understanding, the two parameter arrays are synchronised<br />
in their contexts. The example of prog3 can therefore be applied<br />
to two array parameters x and y by prog3 ≪= (czip (x, y))<br />
A tuple pattern can also be used in a binding statement, typed by<br />
rule [tupB] (Figure 2.2). For example, the following is equivalent<br />
to prog3 by parameter/statement binding exchange (see Section 4):<br />
prog3 ′ = <strong>codo</strong> z ⇒ (x, y) ← current z<br />
a ← laplace1D x<br />
b ← laplace1D y<br />
(current a) + (current b)<br />
[tupP]<br />
[varP]<br />
Γ ⊢ <strong>codo</strong> p ⇒ e : Comonad c ⇒ c t → t ′<br />
Γ, x : c t ⊢c e : t ′<br />
Γ ⊢ (<strong>codo</strong> x ⇒ e) : Comonad c ⇒ c t → t ′<br />
Γ, x : c t, y : c t ′ ⊢c e : t ′′<br />
Γ ⊢ (<strong>codo</strong> (x, y) ⇒ e) : Comonad c ⇒ c (t, t ′ ) → t ′′<br />
[wildP]<br />
Γ ⊢c e : t<br />
Γ ⊢ (<strong>codo</strong> ⇒ e) : Comonad c ⇒ c a → t<br />
[varB]<br />
[tupB]<br />
Γ ⊢c p ← e; e : t<br />
Γ ⊢c e1 : t Γ, y : c t ⊢c e2 : t′<br />
[letB]<br />
3. Desugaring <strong>codo</strong><br />
Γ ⊢c y ← e1; e2 : t ′<br />
Γ ⊢c e1 : (t1, t2)<br />
Γ, x : c t1‘, y : c t2 ⊢c e2 : t ′<br />
Γ ⊢c (x, y) ← e1; e2 : t ′<br />
Γ, ⊢c e1 : t<br />
Γ, x : t ⊢c e2 : t ′<br />
Γ ⊢c let y = e1; e2 : t ′<br />
Figure 1. Typing rules for <strong>codo</strong><br />
The translation of <strong>codo</strong>-<strong>notation</strong> is based on Uustalu and Vene’s<br />
semantics for a context-dependent λ-calculus [17]. The translation<br />
has two parts: translation of binding into composition via cobind<br />
and management of the environment for the local variables bound<br />
in the <strong>codo</strong>-block. The first part of the translation is explained by<br />
considering a restricted <strong>codo</strong>-<strong>notation</strong> where only a single variable,<br />
bound in the previous statement, is in scope of any expression.<br />
Single-variable contexts Consider the following <strong>codo</strong>-block for<br />
some comonad C and unary operations f and g:<br />
foo1 = <strong>codo</strong> x ⇒ y ← f x; g y<br />
The first statement of the block can be interpreted as a function<br />
from the parameter x to its expression:<br />
(λx → f x) : C x → y (1)<br />
The second statement, which is the final result expression, can be<br />
similarly interpreted as a function from y to its expression:<br />
(λy → g y) : C y → z (2)<br />
Both (1) and (2) are functions with structured input, thus the semantics<br />
of foo1 is the comonadic composition of (1) and (2):<br />
�foo1 � = (λy → g y) ◦ (cobind (λx → f x)) : Cy → z.<br />
where we write cobind for the prefix version of ≪=.<br />
Multiple-variable contexts The <strong>codo</strong>-<strong>notation</strong> however provides<br />
multi-variable contexts, allowing the following example with binary<br />
function h : Cx → Cy → z:<br />
foo2 = <strong>codo</strong> x ⇒ y ← f x; h x y<br />
where foo2 : Cx → z. The first statement cannot be interpreted<br />
as before since the second statement uses both x and y, thus its<br />
interpretation must return x along with the result of f x:<br />
(λx → (current x, f x)) : C x → (x, y) (3)<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 4 2012/9/22
Applying current to x means that the cobind of (3), which has<br />
type C x → C (x, y), returns the parameter x and the result of<br />
f x synchronised in their contexts.<br />
The interpretation of the second statement is a function which<br />
takes a value C (x, y) and unzips it, binding the constituent values<br />
to x and y in the scope of h x y:<br />
(λenv → let x = cmap fst env<br />
y = cmap snd env<br />
in h x y) : C (x, y) → z (4)<br />
The variables x and y are still synchronised at the same context<br />
since cmap preserves the contextual properties of the comonad.<br />
The translation of foo2 is the comonadic composition of (3) & (4):<br />
�foo2� = (λenv → let x = cmap fst env<br />
y = cmap snd env<br />
in h x y) ◦ (cobind (λx → (current x, f x)))<br />
3.1 General construction<br />
The translation folds over the list of binding statements in a <strong>codo</strong>block,<br />
accumulating a comonadic environment of the local variables<br />
bound thus far. The accumulated environment is structured<br />
by right-nested tuples terminated by an empty tuple.<br />
Thus, the actual translation of foo2 is:<br />
�foo2� = (λenv → let y = cmap fst env<br />
x = cmap (fst ◦ snd) env<br />
in g x y)<br />
◦ (cobind (λenv → (let x = cmap fst env in f x,<br />
current env)))<br />
◦ (cmap (λenv → (env, ())))<br />
For the first statement of foo2 the environment contains just x<br />
and has type C (x, ()); the environment of the second statement<br />
contains x and y and has type C (y, (x, ())).<br />
The interpretation is defined by a number of auxiliary interpretation<br />
functions which return a Haskell function.<br />
Top-level interpretation The top-level interpretation of a <strong>codo</strong>block<br />
has the form �<strong>codo</strong> p ⇒ e�, defined:<br />
�<strong>codo</strong> x ⇒ e� = �x ⊢ e�c ◦ (cmap (λx → (x, ())))<br />
�<strong>codo</strong> ⇒ e� = �reserved ⊢ e�c ◦ (cmap (λx → (x, ())))<br />
�<strong>codo</strong> (x, y) ⇒ e� = �x, y ⊢ e�c ◦<br />
cmap (λp → (fst p, (snd p, ())))<br />
where �Γ ⊢ b�c is the interpretation for bindings within a <strong>codo</strong>block<br />
where Γ is the context of the local variables and b is the list<br />
of statements in the block terminated by a single expression.<br />
The top-level interpretation generalises easily to arbitrary variabletuple<br />
patterns. In each case, ��c is composed first with a function<br />
that converts the incoming parameter of the <strong>codo</strong>-block into the<br />
correct form to be passed to the result of ��c.<br />
Binding interpretation Given Γ = v0, . . . , vn, then �Γ ⊢ e�c<br />
returns a Haskell function of type where ti is the type of the variable<br />
vi and t is the type of e:<br />
Comonad c ⇒ c (t0, (. . . , (tn, ()))) → t<br />
Thus, the input of the function defined by ��c is comonadic with<br />
right-nested tuples, terminated by the empty tuple, as its values.<br />
The definition of ��c is:<br />
�Γ ⊢ e�c = �Γ ⊢ e�exp<br />
�Γ ⊢ x ← e; e ′ �c = �x, Γ ⊢ e ′ �c ◦<br />
cobind (λenv →<br />
(�Γ ⊢ e�exp env, current env))<br />
The interpretation of an expression, on the right-hand side of a<br />
binder or the last expression in the block, is given by �Γ ⊢ e�exp.<br />
Expression interpretation Finally, �Γ ⊢ e�exp unzips the incoming<br />
comonadic environment binding the values to the variables in<br />
Γ with a local let-binding into the scope of the expression e.<br />
�Γ ⊢ e�exp =<br />
λenv → let {∀vn ∈ Γ} vn = cmap (fst ◦ snd n ) env in e<br />
where snd n means n compositions of snd and snd 0 = id and<br />
{∀vn ∈ Γ} is meta-level directive iterating over the variables in Γ.<br />
Section 6 compares the translation of <strong>codo</strong>-<strong>notation</strong> with that of<br />
the do-<strong>notation</strong>, and explains why the translation of <strong>codo</strong>-<strong>notation</strong><br />
is relatively more complex.<br />
4. Equational theory<br />
As shown in Section 2.1, the cobind operation of a comonad provides<br />
composition for functions with structured input, defined:<br />
(ˆ◦) :: Comonad c ⇒ (c y → z) → (c x → y) → c x → z<br />
g ˆ◦ f = g ◦ (f ≪=)<br />
The laws of a comonad are exactly the laws that guarantee this<br />
composition is associative and has a left and right unit, provided by<br />
current. The right-hand column of Figure 3.1(b) summaries these<br />
properties of composition along with the corresponding comonad<br />
law which provides that property of composition (labelled [C1-3]).<br />
As there is no mechanism for enforcing such rules in Haskell the<br />
programmer is expected to verify the laws on their own.<br />
Given the desugaring of <strong>codo</strong> into the operations of a comonad,<br />
the comonad laws imply equational rules on the syntax of <strong>codo</strong><strong>notation</strong><br />
which is given in the left-hand column of Figure 3.1(b).<br />
Figure 3.1(a) gives properties of the the top-level <strong>codo</strong>-block<br />
which following from its translation.<br />
Section 2.2 introduced the ComonadZip type class which provides<br />
the operation: czip : (c a, c b) → c (a, b). This operation<br />
is that of a (semi)-monoidal functor which may satisfy various<br />
laws with respect to the comonad (see the discussion of (semi)monoidal<br />
comonads in [17]). The following property, which we<br />
call idempotency of the semi-monoidal functor, frequently holds<br />
of comonad/czip implementations:<br />
czip (x, x) ≡ cmap (λx → (x, x)) x (5)<br />
This property implies syntactic laws on <strong>codo</strong> shown in Figure<br />
3.1(c), relating tuple patterns and the use of czip. For each<br />
rule involving a tuple pattern there is an equivalent rule derived<br />
from the parameter/statement exchange rule in Figure 3.1(a).<br />
There are many possible laws which depend on other conditions<br />
or operations. We included the Figure 3.1(c) in particular as the<br />
idempotency property frequently holds.<br />
4.1 Shape preservation<br />
An interesting derived property of comonads is shape preservation.<br />
The shape of a data type is its structure without any values i.e.<br />
shape = cmap (const ())<br />
where const x = λ → x. For example, the shape of a list is just<br />
the cons-cells, without any values, or in this case the unit value ().<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 5 2012/9/22
<strong>codo</strong> x ⇒ z ← (<strong>codo</strong> y ⇒ e1) x<br />
e2<br />
Syntactic property Comonad property<br />
(a). Pure rules<br />
<strong>codo</strong> x ⇒ f x ≡ f (<strong>codo</strong>-η)<br />
≡ <strong>codo</strong> x ⇒ y ← current x<br />
z ← e1<br />
<strong>codo</strong> (x, y) ⇒ e ≡ <strong>codo</strong> z ⇒ (x, y) ← current z<br />
e<br />
<strong>codo</strong> x ⇒ y ← current x<br />
f y<br />
<strong>codo</strong> x ⇒ y ← f x<br />
current y<br />
<strong>codo</strong> y ⇒ w ← e1<br />
<strong>codo</strong> x ⇒ z ← e2<br />
e3) y<br />
e2<br />
(b). Comonad laws<br />
(<strong>codo</strong>-β)<br />
(parameter/statement binding exchange)<br />
≡ <strong>codo</strong> x ⇒ f x (right unit) f ˆ◦ ˆ<br />
id ≡ f<br />
⇒ [C1] current ≪= x ≡ x<br />
≡ <strong>codo</strong> x ⇒ f x (left unit) ˆ id ˆ◦ f ≡ f<br />
⇒ [C2] current (f ≪=x) ≡ f x<br />
≡ <strong>codo</strong> x ⇒ y ← current x<br />
w ← e1<br />
z ← e2<br />
e3<br />
≡ <strong>codo</strong> x ⇒ z ← (<strong>codo</strong> y ⇒ w ← e1<br />
e2) x<br />
e3<br />
(c). Additional rules (if conditions met)<br />
<strong>codo</strong> (b, c) ⇒ f (czip (b, c)) ≡ <strong>codo</strong> (b, c) ⇒ z ← (current b, current c)<br />
f z<br />
<strong>codo</strong> x ⇒ (a ′ , b ′ ) ← czip (a, b)<br />
f a ′ b ′<br />
≡ <strong>codo</strong> x ⇒ f a b<br />
A consequence of the comonad laws is that, for any function<br />
f : c a → b, the function (cobind f ) : c a → c b preserves the<br />
shape of the incoming structure in its result. For example, with the<br />
list comonad of Section 2.1, for any function f :[a ] → b, cobind f<br />
is a function which given a list of length n returns a list of length<br />
n. This shape preservation property can be state formally as:<br />
(∀f) shape ◦ (cobind f ) ≡ shape (6)<br />
Appendix A gives the proof, which holds for all comonads.<br />
5. Further examples<br />
So far we have seen just two example comonads: non-empty lists<br />
and cursored arrays. Here we show three more examples, of numerical<br />
functions, streams, and dynamic programming. These three<br />
examples will be united by a single comonad called the costate, or<br />
in-context comonad. The stream and dynamic programming examples<br />
make use of recursively defined comonad operations.<br />
5.1 Parameterised Computations<br />
A comonad of parameterised values has data type C a = x → a<br />
where x is a fixed parameter type which has a monoidal structure.<br />
An instance with real-number parameters (i.e. x = R) can be used<br />
to structure numerical analysis problems over numerical functions.<br />
In Haskell, the comonad of R-parameterised values is defined:<br />
instance Comonad ((→) Double) where<br />
current f = f 0.0<br />
k≪=f = λx ′ → k (λx → f (x + x ′ ))<br />
The cobind operation takes a comonadic operation k : (Double →<br />
a) → b and numerical function f : Double → a returning a<br />
Figure 2. Equational laws for the <strong>codo</strong>-<strong>notation</strong><br />
(associativity) h ˆ◦ (g ˆ◦ f) ≡ (h ˆ◦ g) ˆ◦ f<br />
⇒ [C3] g ≪= (f ≪= x)<br />
≡ (λx ′ → g (f ≪= x ′ )) ≪= x<br />
(iff x is not free in e1)<br />
– (idempotent monoidal functor)<br />
czip (x, x) =<br />
———-cmap (λx → (x, x)) x<br />
transformed numerical function f ′ = k≪=f : Double → b where<br />
the value of f ′ at x ′ is calculated by k applied to the function f<br />
shifted by x ′ . In the contextual intuition, the context is the amount<br />
by which f has been shifted. The current operation returns the value<br />
of the function at the origin. In the contextual intuition the origin<br />
provides the value at the current context (the current shift position).<br />
An example operation is numerical differentiation defined:<br />
differentiate f = (f 0.01 − f 0.0) / 0.01<br />
Since cobind passes the f shifted by x ′ to differentiate then f 0.0<br />
means f x ′ . The value by which f is shifted is unknown within a<br />
comonadic operation thus the comonad is context-oblivious.<br />
The following operation computes which values of a function<br />
are local minima by testing the first and second derivatives:<br />
minima = <strong>codo</strong> f ⇒ f ′ ← differentiate f<br />
f ′′ ← differentiate f ′<br />
(current f ′ ≈ 0) ∧ (current f ′′ < 0)<br />
Another useful numerical analysis operation is to approximate<br />
a functions as a series of polynomials, such as in the MacLaurin<br />
series defined:<br />
f(x) ≈ f(0) + f ′ (0)<br />
1! x + f ′′ (0)<br />
2! x2 + f ′′′ (0)<br />
3! x3 + . . .<br />
Since the context i.e. the amount by which a function has been<br />
shifted is unknown, a MacLaurin operation for the numerical function<br />
comonad is problematic because (1) each term of the series<br />
uses the current context x in its calculation (2) each term accesses<br />
the value of the corresponding derivative at the origin. Since f is<br />
shifted by an unknown amount the actual origin value cannot be accessed.<br />
A MacLaurin operations requires the context to be known.<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 6 2012/9/22
A possible solution is to transform the parameter numerical<br />
function such that it remembers its context by returning a pair of<br />
f x and x itself i.e. λx → (f x, x). A tuple pattern can then split<br />
this input into the original parameter function f and essentially the<br />
identity function which provides the corresponding context x:<br />
m3 :: (Double → (Double, Double)) → Double<br />
m3 = <strong>codo</strong> (f , x) ⇒ f ′ ← differentiate f<br />
f ′′ ← differentiate f ′<br />
f (−current x)<br />
+ (f ′ (−current x)) ∗ (current x)<br />
+ (f ′′ (−current x)) / 2 ∗ (current x) ∗∗ 2<br />
where x : Double → Double and f : Double → Double.<br />
Thus, the MacLaurin approximation of a function can be computed:<br />
maclaurin3 f = m3 ≪=(λx → (f x, x))<br />
Context-oblivious and context-aware operations The operations<br />
on numerical functions here fall into two categories:<br />
1. context-oblivious e.g. pointwise ops, differentiate, minima<br />
2. context-aware e.g. MacLaurin series.<br />
Context-oblivious operations either do not use the value of the<br />
context, such as pointwise operations, or only use it relatively such<br />
as differentiate or the laplace1D operator from Section 2.2.<br />
Context-aware operations have their behaviour determined by<br />
the context. For example, in the MacLaurin series the value of the<br />
context is used in the calculation and is used to access absolute<br />
contexts (the origin). Since the numerical function comonad uses<br />
shifts, the current context is unknown. The work-around above used<br />
tuple patterns and a transformation of a numerical function into a<br />
function returning value-parameter pairs.<br />
The pattern of value-parameter pairs can be abstracted as a<br />
comonad of parameterised values paired with a particular parameter<br />
Ca = (x → a) × x (resembling cursored arrays). This<br />
comonad is called the costate comonad [17] 1 although we prefer<br />
the name in-context comonad as ‘costate’ is less intuitive. The data<br />
type is:<br />
data InContext c a = InContext (c → a) c<br />
where c is the type of contexts. The InContext comonad for any<br />
context type is defined:<br />
instance Comonad (InContext c) where<br />
current (InContext a c) = x c<br />
(InContext x c) =≫ k =<br />
InContext (λc ′ → k (InContext x c ′ )) c<br />
Thus, current extracts the current context and cobind returns an incontext<br />
value whose value at context c ′ is computed from k applied<br />
to the parameter in-context value at c ′ .<br />
The MacLaurin example can be rewritten:<br />
m3 :: InContext Double Double → Double<br />
m3 = <strong>codo</strong> f ⇒ f ′ ← differentiate f<br />
f ′′ ← differentiate f ′<br />
x ← context f<br />
(f ‘at‘ 0) + (f ′ ‘at‘ 0) ∗ (current x)<br />
+ (f ′′ ‘at‘ 0) / 2 ∗ (current x) ∗∗ 2<br />
using helper functions on contexts:<br />
1 The phrase costate is due to the relation between the state monad and<br />
costate comonad, which are induced from the same pair of adjoint functors.<br />
The product functor DX = A × X, which is left adjoint to the exponent<br />
functor T X = X → A, thus D ⊣ T , induces the state monad T DA =<br />
X → (A × X) and the costate comonad DT A = (X → A) × X.<br />
context (InContext x c) = c<br />
at (InContext x c) c ′ = x c ′<br />
The InContext comonad has many practical uses, for example the<br />
cursored array example is isomorphic to an instance of InContext<br />
with a context of indices within the arrays boundaries. The next<br />
example uses the InContext comonad for stream programming.<br />
5.2 Streams for Dataflow Programming<br />
Uustalu and Vene showed that the Lucid dataflow language can be<br />
given a semantics in terms of a stream comonad, providing an interpreter<br />
of higher-order Lucid programs written in an AST format<br />
i.e. a deep-embedded DSL is provided [18]. Using <strong>codo</strong>-<strong>notation</strong><br />
the first-order fragment of Lucid can be embedded directly into<br />
Haskell as a shallow-embedded DSL where the stream comonad<br />
is the InContext comonad with Int contexts:<br />
type Stream a = InContext Int a<br />
The behaviour of cobind can be illustrated for InContext Int a by<br />
the following informal definition, where 〈a, b, c, . . .〉@x represents<br />
an infinite stream in context x:<br />
〈x0, x1, . . .〉@c =≫ f = 〈f 〈x0, x1, . . .〉@0,<br />
f 〈x0, x1, . . .〉@1, . . .〉@c<br />
Lucid is essentially a calculus of recursive, infinite stream equations.<br />
Two key combinators are next and fby (followed by) which<br />
have behaviour [19]:<br />
next 〈x0, x1, x2, . . .〉 = 〈x1, x2, . . .〉<br />
〈x0, x1, x2, . . .〉 fby 〈y0, y1, y2, . . .〉 = 〈x0, y0, y1, . . .〉<br />
The next operator performs lookahead in a stream. The fby operator<br />
delays a stream by the first element of another and is useful for<br />
defining guarded recursion. For example, the stream of natural<br />
numbers can be defined recursively in Lucid as:<br />
n = 0 fby n + 1<br />
where 0 and 1 are constant streams and + is pointwise addition.<br />
In Haskell, the core non-pointwise operations of Lucid can be<br />
defined for InContext as:<br />
fby :: Stream a → Stream a → a<br />
fby (InContext s c) (InContext t d) =<br />
if (c ≡ 0 ∧ d ≡ 0) then s 0 else t (d − 1)<br />
next :: Stream a → a<br />
next (InContext s c) = s (c + 1)<br />
constant :: a → Stream a<br />
constant x = InContext (λ → x) 0<br />
To define the stream of natural numbers we first define a nonrecursive<br />
operator with parameter n for the recursive result:<br />
nat ′ :: Num a ⇒ Stream a → a<br />
nat ′ = <strong>codo</strong> n ⇒ n ′ ← (current n) + 1<br />
(constant 0) ‘fby‘ n ′<br />
The recursive “knot” must be tied such that the result of cobind on<br />
nat ′ is passed to itself. A suitable combinator is:<br />
cfix :: Comonad c ⇒ (c a → a) → c a<br />
cfix f = f ≪= (cfix f )<br />
The stream of natural numbers can then be calculated by:<br />
nat = cfix nat ′ ≡ 〈0, 1, 2, . . .〉@⊥<br />
The definition of cfix results in an undefined cursor for nat since<br />
the operation c a → a recursively defines just the a-valued parts<br />
of the stream. In some cases, a well-defined cursor is preferable. An<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 7 2012/9/22
alternative approach internalises the definition of cfix by binding a<br />
recursive call in the <strong>codo</strong>-block e.g.<br />
nat = <strong>codo</strong> n ⇒ n ′ ← (nat n) + 1<br />
(constant 0) ‘fby‘ n ′<br />
The stream of natural numbers can thus be applied by passing<br />
in any stream to the cobind of nat which provides the stream<br />
structure, including the cursor, but has its values ignored:<br />
(nat≪=(constant ())) ≡ 〈0, 1, 2, . . .〉@0<br />
An example using next is the howfar operation (defined in the<br />
original Lucid book [19]) which takes an input stream of numbers<br />
and returns a stream of the “distance” to a value of 0 in the input<br />
stream at the corresponding position e.g.<br />
howfar 〈6, 1, 5, 0, 3, 0, . . .〉 = 〈3, 2, 1, 0, 1, 0, . . .〉<br />
The howfar function can be written recursively as:<br />
howfar = <strong>codo</strong> x ⇒ x ′ ← next x<br />
if (current x ≡ 0)<br />
then 0 else 1 + (howfar x ′ )<br />
5.3 Dynamic programming<br />
Lucid can be generalised to multi-dimensional streams essentially<br />
modelling infinite arrays [3]. Such computations have the same<br />
comonadic structure as before, using InContext, but with contexts<br />
of Int tuples. Multi-dimensional recursive streams are convenient<br />
for defining dynamic programming problems [15].<br />
We generalise here the Lucid example to multi-dimensional<br />
streams with a specialised comonad for two-dimensional dynamic<br />
programming problems with type:<br />
data DynP x a = DynP (InContext (Int, Int) a) x x<br />
where x is the type of the initial parameters to the algorithm. We<br />
give a brief description of the Levenshtein algorithm for computing<br />
the edit distance between two strings as an example to show the<br />
power of comonads and <strong>codo</strong>. The definition in <strong>codo</strong>-<strong>notation</strong> is:<br />
levenshtein :: DynP String Int → Int<br />
levenshtein =<br />
<strong>codo</strong> x ⇒ -- Initialise first row and column<br />
d ← levenshtein x -- recursive call<br />
dn ← (current d) + 1<br />
d0 ← (constant 0) ‘fbyX ‘ dn<br />
d ′ ← d0 ‘fbyY ‘ dn<br />
-- Shift (-1, 0), (0, -1), (-1, -1)<br />
d w ← d ‘at‘ (−1, 0)<br />
d n ← d ‘at‘ (0, −1)<br />
d nw ← d ‘at‘ (−1, −1)<br />
d ′′ ← if (paramX d ≡ paramY d)<br />
then current d nw<br />
else minimum [(current d w) + 1,<br />
(current d n) + 1,<br />
(current d nw) + 1]<br />
d ′ ‘thenXY ‘ d ′′<br />
Lines 2-5 in the <strong>codo</strong>-block recursively define the top-most<br />
row and left-most column of the two-dimensional as the stream of<br />
natural numbers using two-dimensional followed by operators e.g.<br />
fbyX :: DynP x a → DynP x a → a<br />
fbyX (DynP (InContext s (c1 , c2 ) ))<br />
(DynP (InContext t (c1 ′ , c2 ′ ) ))<br />
= if (c1 ≡ 0 ∧ c1 ′ ≡ 0) then s (0, c2 )<br />
else t (c1 ′ − 1, c2 ′ )<br />
The paramX and paramY operations extract elements from initial<br />
parameters of the algorithm at the current context in the x and y<br />
dimensions respectively e.g.<br />
paramX :: DynP String a → Char<br />
paramX (DynP (InContext c@(c1 , c2 )) x y) = x !! c1<br />
The thenXY operator is a combination of next and fby operations<br />
over both dimensions which takes two infinite arrays and<br />
combining them such that the first column and row are from the<br />
first parameter and the rest of the elements from the second:<br />
thenXY :: DynP x a → DynP x a → a<br />
thenXY (DynP (InContext s (c1 , c2 )) )<br />
(DynP (InContext t (c1 ′ , c2 ′ )) ) =<br />
if ((c1 ≡ 0 ∧ c1 ′ ≡ 0) ∨ (c2 ≡ 0 ∧ c2 ′ ≡ 0))<br />
then s (c1 , c2 )<br />
else t (c1 ′ , c2 ′ )<br />
A sample usage, with some suitable output function, is:<br />
> let x = DynP (InContext ⊥ (0, 0)) (" hello") (" hey")<br />
> levenshtein ≪= x<br />
’ ’ ’h’ ’e’ ’l’ ’l’ ’o’<br />
’ ’ [0, 1, 2, 3, 4, 5]<br />
’h’ [1, 0, 1, 2, 3, 4]<br />
’e’ [2, 1, 0, 1, 2, 3]<br />
’y’ [3, 2, 1, 1, 2, 3]<br />
where ⊥ can be used for the initial value of the InContext part<br />
of DynP since the values of the parameter are never accessed.<br />
Efficiency Functions in Haskell are not memoized (in their inputs<br />
and outputs) thus the Lucid and dynamic programming examples<br />
are inefficient incurring much recalculation. A more efficient<br />
solution can be provided by isomorphic non-function representations<br />
which Haskell memoizes by its call-by-need semantics, such<br />
as lists for streams and arrays for the dynamic programming.<br />
6. Comparing <strong>codo</strong>- and do-<strong>notation</strong><br />
While comonads and monads are dual, the <strong>codo</strong>- and do-<strong>notation</strong><br />
(for programming with monads) do not appear to be dual. Both provide<br />
let-binding syntax for composition of comonadic and monadic<br />
operations respectively. However, <strong>codo</strong>-blocks are parameterised,<br />
with type c a → b for a comonad c, whilst do-blocks are unparameterised,<br />
with type m a for a monad m. Since comonads abstract<br />
functions with structured input the parameter to a <strong>codo</strong>-block<br />
is important. In the do-<strong>notation</strong>, expressions have implicit input via<br />
their free variables where Haskell’s own scoping mechanism can<br />
be reused for handling the local variables in a do-block.<br />
Section 6.1 explains the reasons behind the <strong>codo</strong>- and do<strong>notation</strong><br />
differences in more detail, and also shows why the translation<br />
of the <strong>codo</strong>-<strong>notation</strong> is more complicated than the translation<br />
of the do-<strong>notation</strong>. Section 6.2 compares the <strong>notation</strong>s directly for<br />
computations with implicit parameters which can structured equivalently<br />
by the reader monad and the product (coreader) comonad.<br />
6.1 Asymmetry in the <strong>notation</strong>s<br />
The distinction between <strong>codo</strong>- and do-<strong>notation</strong> can be understood<br />
from the perspective of the <strong>notation</strong>s as internal domain-specific<br />
languages, for contextual and effectful computations respectively,<br />
with their semantics defined by the translation to Haskell. This<br />
approach is similar to that of categorical semantics where a typed<br />
program is given a de<strong>notation</strong> as a morphism 2 , in some category,<br />
mapping from the inputs of a program to the outputs.<br />
2 Morphisms generalise the notion of function. Readers unfamiliar with<br />
category theory may safely replace ‘morphism’ with ‘function’ here.<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 8 2012/9/22
Categorical semantics approach For the simply-typed λ-calculus,<br />
the traditional approach pioneered by Lambek and Scott recursively<br />
maps the type derivation of an expression to a morphism [7]:<br />
�Γ ⊢ e : τ� : (�τ0� × . . . × �τn�) −→ �τ�<br />
where Γ = x0 : τ0, . . . xn : τn. Thus, an expression e of type τ<br />
with a context of free-variable typing assumptions Γ is modelled as<br />
a morphism which maps from a product of the values of the free<br />
variables as inputs to a result as the output. From now on we will<br />
elide �� brackets on types in the morphisms for brevity.<br />
Categorical semantics for effectful computations Moggi showed<br />
that effectful computations can be given a semantics in terms of a<br />
Kleisli category which has morphisms with structured output of<br />
type a → m b for a monad m [8, 9]. The de<strong>notation</strong>s are:<br />
�Γ ⊢ e : τ� : (�τ0� × . . . × �τn�) −→ m �τ�<br />
again where Γ = x0 : τ0, . . . xn : τn.<br />
In Moggi’s calculus, let-binding follows the traditional approach<br />
of internalising substitution (whether this is call-by-value<br />
or call-by-name with respect to effects depends on the particular<br />
monad). The semantics for let-binding corresponds to composition<br />
of the de<strong>notation</strong>s, provided by the bind operation of a monad.<br />
However, handling multi-variable environments requires a strong<br />
monad which has an additional operation called strength. The effectful<br />
semantics for let-binding is:<br />
��<br />
(where f : a → b is written a b and composition is<br />
expressed by concatenation of two arrows)<br />
�Γ ⊢ e : τ� = g : Γ → m τ<br />
�Γ, x : τ ⊢ e ′ : τ ′ � = g ′ : Γ × τ → m τ ′<br />
�Γ ⊢ let x = e in e ′ : τ ′ � :=<br />
Γ 〈id,g〉 ��<br />
Γ × m τ strength ��<br />
m (Γ × τ)<br />
f<br />
bind g′<br />
��<br />
m τ ′<br />
where 〈f, g〉 is the function pairing: λx → (f x, g x), bind is<br />
the prefix version of Haskell’s >= operator and strength provides<br />
distributivity of × over m:<br />
strength : (a × m b) → m (a × b)<br />
bind : (a → m b) → (m a → m b)<br />
The do-<strong>notation</strong> does not provide a full semantics for a language<br />
with effects, but instead provides just a semantics for effectful letbinding<br />
embedded in Haskell. This embedding can be simplified by<br />
reusing Haskell’s scoping mechanism.<br />
The do-<strong>notation</strong> In Haskell, all monads are strong with a canonical<br />
strength operator:<br />
strength :: Monad m ⇒ (a, m b) → m (a, b)<br />
strength (a, mb) = mb >= (λb → return (a, b))<br />
It is straightforwardly proved that this definition of strength satisfies<br />
the properties of a strong monad (see [8] for these properties).<br />
This definition of strength uses just the core monad operations<br />
and the scoping mechanism of Haskell. Using this definition<br />
of strength, and simplifying according to the monad laws, gives a<br />
the semantics where Haskell’s scoping mechanism alone provides<br />
the semantics of multi-variable contexts for effectful let-binding.<br />
Thus the translation of do is relatively simple:<br />
Γ ⊢ e : m τ Γ, x : τ ⊢ e ′ : m τ ′<br />
Γ ⊢ �do x ← e; e ′ � : m τ ′ ≡ Γ ⊢ e >= (λx → e ′ ) : m τ ′<br />
Thus the inputs to effectful computations need not be made explicit<br />
and a do-block has type m a.<br />
Categorical semantics for contextual computations The dual of<br />
Moggi’s semantics interprets expressions in a coKleisli category<br />
whose morphisms have structured input (c a → b for a comonad<br />
c). The de<strong>notation</strong>s are given by:<br />
�Γ ⊢ e : τ� : c (�τ0� × . . . × �τn�) −→ �τ�<br />
For a comonadic semantics the structure c is over the context of<br />
free-variables. Contextual let-binding with multi-variable contexts<br />
can be defined similarly to let for effectful computations:<br />
�Γ ⊢ e : τ� = g : c Γ → τ<br />
�Γ, x : τ ⊢ e ′ : τ ′ � = g ′ : c (Γ × τ) → τ ′<br />
�Γ ⊢ let x = e in e ′ : τ ′ � :=<br />
c Γ<br />
〈id,cobind g〉<br />
��<br />
c Γ × cτ cstrength ��<br />
c (Γ × τ)<br />
Again, a strength-like operation is used, of type:<br />
cstrength :: Comonad c ⇒ (c a, c b) → c (a, b)<br />
g ′<br />
��<br />
τ ′<br />
which corresponds to the czip operation of a semi-monoidal<br />
comonad (see Section 4). Whilst strength for monads has a canonical<br />
definition in terms of bind, return, and Haskell’s scoping mechanism<br />
there is no such definition of cstrength which satisfies the<br />
required properties (which are the laws of monoidal comonad [17]).<br />
Thus the scoping mechanism of Haskell cannot be reused as it does<br />
not provide comonadic structuring of the variables in the context.<br />
Thus, for the <strong>codo</strong>-<strong>notation</strong> the multi-variable comonadic contexts<br />
must be modelled explicitly as seen in the translation of <strong>codo</strong><strong>notation</strong><br />
(Section 3) resulting in the relatively more complicated<br />
translation of <strong>codo</strong>-<strong>notation</strong> compared with that of do-<strong>notation</strong>.<br />
In our translation of <strong>codo</strong> an alternate approach is used:<br />
�Γ ⊢ e : τ� = g : c Γ → τ<br />
�Γ, x : τ ⊢ e ′ : τ ′ � = g ′ : c (Γ × τ) → τ ′<br />
�Γ ⊢ let x = e in e ′ : τ ′ � :=<br />
c Γ cobind〈current,g〉 ��<br />
c (Γ × τ)<br />
g ′<br />
��<br />
′<br />
τ<br />
where 〈current, g〉 : c Γ → Γ × τ.<br />
Although this approach does not provide any simplifications in<br />
terms of the handling of contexts it does not require a definition<br />
of cstrength/czip. This approach is equivalent to the cstrength<br />
approach if cstrength is idempotent (see Section 4).<br />
6.2 Implicit parameters in <strong>codo</strong>- and do-<strong>notation</strong><br />
The product comonad, sometimes called the coreader comonad,<br />
is a simple comonad encoding the notion of implicit parameters<br />
threaded through a computation. It has the following definition:<br />
type CoReader p a = (a, p)<br />
instance Comonad ((, ) p) where<br />
current (p, a) = a<br />
(p, a) =≫ f = (p, f (p, a))<br />
Thus cobind simply passes the implicit parameter unchanged to the<br />
parameter function and to the output of the cobind. The implicit<br />
parameter can be accessed by the operation:<br />
ask (p, a) = p<br />
Interestingly, the product/coreader comonad is isomorphic to<br />
the reader monad 3 . The reader monad also structures computations<br />
3 Naming the product comonad ‘coreader’ is perhaps misleading: the product<br />
is isomorphic to the reader monad and thus provides the same functionality<br />
but it is not a dual (whatever that might mean).<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 9 2012/9/22
with an implicit parameter, abstracting the threading of the parameter<br />
through a computation. The reader monad has the following<br />
type and monad instance:<br />
type Reader p a = p → a<br />
instance Monad ((→) p) where<br />
return x = (λ → x)<br />
x >= f = (λp → f (x p) p)<br />
Thus bind returns a computation with a parameter x that is passed<br />
to the argument and result of a monadic operation. As with the<br />
product comonad an ask operation returns the implicit parameter:<br />
askM :: Reader p p<br />
askM = id<br />
The reader monad and coreader comonad capture the same notion<br />
of computation since there is an isomorphism between the monadic<br />
and comonadic operations provided by curry/uncurry:<br />
(a × p) → b<br />
��<br />
uncurry<br />
curry ��<br />
(a → (p → b))<br />
The following example uses the reader monad with do-<strong>notation</strong><br />
to abstract an implicit parameter, where f :String → Reader Int Int<br />
is some function which may depend on the implicit parameter:<br />
foo :: Reader Int Int<br />
foo = do x ← f "test"<br />
y ← askM<br />
return (x ∗ y)<br />
The equivalent example in the <strong>codo</strong>-<strong>notation</strong>, with f ′ = (uncurry f ):<br />
CoReader String Int → Int, is:<br />
foo ′ :: CoReader a Int → Int<br />
foo ′ = <strong>codo</strong> ⇒ s ← "test"<br />
x ← f ′ s<br />
y ← ask x<br />
(current x) ∗ (current y)<br />
There are three significant features of foo ′ .<br />
Firstly, foo ′ has parameter matched by the wildcard pattern (see<br />
rule [wildP], Figure 2.2) thus the values within the input structure<br />
are never used. The parameter to the block is still required since it<br />
provides the implicit parameter of type Int to the block.<br />
Secondly, the argument to f ′ is first bound to a variable since it<br />
must be imbued with the comonadic structure to pass the implicit<br />
parameter to f ′ .<br />
Thirdly, ask is passed a parameter x but any local variable<br />
(s, x, or y) would suffice since ask ignores the values of the<br />
input structure and extracts only the implicit parameter from the<br />
incoming CoReader structure. By shape preservation every local<br />
variable has the (same) implicit parameter.<br />
Since the intention of CoReader is to abstract implicit parameters<br />
it seems a shame that ask should have to be given an argument<br />
at all since any local variable will pass the current context. To simplify<br />
such cases, the <strong>codo</strong>-<strong>notation</strong> provides allows a wildcard as<br />
an expression where :: c (). Thus, the current comonadic structure<br />
of the <strong>codo</strong>-block can be passed, ignoring any particular values<br />
inside the structure. This <strong>notation</strong> dualises the use of a wildcard pattern<br />
in the bindings of do-<strong>notation</strong> e.g. ← print "42" where the<br />
intention is that only the structure, or effects, are important, not the<br />
values inside the structure.<br />
The wildcard expression can be used in previous examples.<br />
For example, the recursively defined natural number stream had a<br />
parameter providing only the stream structure, not any values, thus<br />
it can be rewritten:<br />
nat = <strong>codo</strong> ⇒ n ′ ← (nat ) + 1<br />
(constant 0) ‘fby‘ n ′<br />
Note that the <strong>codo</strong>-<strong>notation</strong> does not allow a wildcard pattern in<br />
a binding statement since by shape preservation such a binding is<br />
redundant as binding does not affect a computation in itself. Thus:<br />
<strong>codo</strong> x ⇒ ← f x; g x ≡ <strong>codo</strong> x ⇒ g x<br />
7. Related Work<br />
7.1 Rebindable syntax<br />
GHC provides an extension for rebindable-syntax, allowing Haskell’s<br />
core syntax to be overloaded. For the do-<strong>notation</strong> the rebindablesyntax<br />
extension suppresses the usual requirement that the operations<br />
in the desugaring of do-<strong>notation</strong> belong to the Monad type<br />
class. Instead the do-<strong>notation</strong> uses whatever >= operation is in<br />
scope, regardless of its type.<br />
Can the rebindable-syntax extension be used to implement<br />
<strong>codo</strong>-<strong>notation</strong> by defining bind as cobind? Both bind and cobind<br />
take a parameter function, but for bind it is the second parameter<br />
while for cobind it is the first:<br />
( >=) :: m a → (a → m b) → m b<br />
(≪=) :: (c a → b) → c a → c b<br />
Thus, cobind should be flipped in the definition of bind:<br />
x >= f = flip (≪=)<br />
Recall that: �do x ← e1; e2� = e1 >= (λx → e2).<br />
Therefore, an expression bound to a variable must have type c a. A<br />
comonadic function could thus be wrapped in do-<strong>notation</strong> as such:<br />
foo x = do {x ′ ← x; f x ′ }<br />
which desugars to (λx → x >= (λx ′ → f x ′ )) i.e. cobind f .<br />
Composing operations is more difficult; the following is no use:<br />
foo2 x = do {x ′ ← x; y ′ ← f x ′ ; g y ′ }<br />
since f x ′ : b not f x ′ : c b. Instead, a nested do can be used to<br />
pass an intermediate value to the next operation:<br />
foo2 x = do y ← do {x ′ ← x; f x ′ }<br />
g y<br />
The examples become increasingly inscrutable as more operations<br />
are composed and multiple-parameter operations are used. Thus<br />
rebinding the syntax of do does not provide a useful candidate for<br />
a <strong>notation</strong> for programming with comonads.<br />
7.2 Arrow <strong>notation</strong><br />
The arrow <strong>notation</strong> in Haskell provides a syntax for working with<br />
abstraction notions of computation [5, 12]. Syntax for application,<br />
abstraction (lambdas), and binding is provided abstracting over the<br />
operations of some notion of computation defined as a category<br />
with additional arrow operations for constructing computations<br />
and extending environments. Arrow computations are defined by<br />
a doubly-parametric data type (i.e. of kind ∗ → ∗ → ∗) and an<br />
instance of the Category and Arrow classes:<br />
class Category cat where<br />
id :: cat a a<br />
(◦) :: cat b c → cat a b → cat a c<br />
class Category a ⇒ Arrow a where<br />
arr :: (x → y) → a x y<br />
first :: a b c → a (b, d) (c, d)<br />
These operations are the minimal set of from which other arrow<br />
combinators can be derived. A Category thus has a notion of<br />
composition and identity for the morphisms, which are modelled<br />
by the type cat x y. The Arrow class provides arr for promoting a<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 10 2012/9/22
Haskell function to a morphism of the category and first transforms<br />
a morphism to take and return an extra parameter, which is used for<br />
threading an environment through a computation.<br />
Every comonad has an associated coKleisli category with morphisms<br />
of structured input c a → b and the composition defined<br />
in Section 2.1. All coKleisli categories are arrows where arr is<br />
defined by pre-composing a function with current, and first is defined<br />
similarly to the translation of binders threading an environment<br />
through a computation in Section 3. The definitions are:<br />
data CoKleisli c a b = CoK {unCoK :: (c a → b)}<br />
instance Comonad c ⇒ Category (CoKleisli c) where<br />
id = CoK current<br />
(CoK g) ◦ (CoK f ) = CoK (g ◦ (cobind f ))<br />
instance Comonad c ⇒ Arrow (CoKleisli c) where<br />
arr k = CoK (k ◦ current)<br />
first (CoK f ) = CoK (λx → (f (cmap fst x),<br />
current (cmap snd x)))<br />
The arrow <strong>notation</strong> comprises syntax for:<br />
– abstraction proc x → e<br />
– application f -< x<br />
– binding x ← e<br />
Thus, given the coKleisli instances for Category and Arrow,<br />
comonadic operations can be written in the <strong>notation</strong>. For example,<br />
consider the following <strong>codo</strong>-block for the array comonad:<br />
<strong>codo</strong> a ⇒ b ← laplace1D a<br />
c ← plus a b<br />
d ← c ‘at‘ 1<br />
if (current d > 1) then 1 else current d<br />
This example uses both unary and binary operators, including at<br />
which is a binary operator with one comonadic parameter and<br />
one pure (i.e. non-comonadic) parameter, and includes a larger<br />
expression that is not a simple function application.<br />
This operation can be rewritten in the arrow syntax as:<br />
proc a → b ← laplace1D ′ -< a<br />
c ← plus ′<br />
-< (a, b)<br />
d ← (at ′ 1) -< c<br />
CoK (λx → if (current x > 1) then 1<br />
else current x) -< d<br />
plus ′ = CoK (λx → plus (cmap fst x) (cmap snd x))<br />
at ′ i = CoK (λx → x ‘at‘ i)<br />
laplace1D ′ = CoK laplace1D<br />
For unary function application the arrow <strong>notation</strong> is not much more<br />
complicated than <strong>codo</strong>, requiring just the explicit application operator.<br />
For multi-parameter operators e.g. a binary operator of type<br />
c x → c y → z, the operator must be transformed to the type<br />
c (x, y) → z e.g. plus ′ above, but the <strong>notation</strong> itself does not<br />
become significantly more complex. For multi-parameter operators<br />
with some pure parameters some transformation is needed such that<br />
the pure parameters are passed first e.g. at ′ above. More complicated<br />
expressions, that are not just the application of a function,<br />
must be packaged into an operation first e.g. the conditional expression<br />
above, which becomes more obfuscated.<br />
Comparing the two approaches, the arrow <strong>notation</strong> appears to<br />
be as powerful as the <strong>codo</strong>-<strong>notation</strong> in terms of the operations<br />
which can be defined. However, whilst the arrow <strong>notation</strong> is almost<br />
as simple as the <strong>codo</strong>-<strong>notation</strong> for some operations, there are<br />
other operations where the syntax is much less natural. In general,<br />
the <strong>codo</strong>-<strong>notation</strong> provides a more elegant, natural solution to programming<br />
with comonads.<br />
Data type Usage<br />
c a = a × x (coreader, product) implicit parameters,<br />
additional parameters e.g. dynamic programming<br />
inputs<br />
c a = x → a (exponent) numerical functions,<br />
context-oblivious streams<br />
c a = (x → a) × x (costate, in-context) streams, arrays,<br />
general containers (for restricted x),<br />
context-aware comonads<br />
c a = [a ] (list) simple array-like comonad<br />
c a = Array i a × i (cursored array) stencil computations,<br />
dynamic programming<br />
Figure 3. Summary of comonad examples in this paper<br />
8. Further <strong>notation</strong>s<br />
We speculate that, since comonads abstract computations with<br />
structured input then pattern matching, used for deconstructing a<br />
data type often in the input of a function, may have special relation<br />
to comonadic programming.<br />
The authors’ previous work on a domain-specific language for<br />
array computations has included a special pattern matching construction<br />
for comonadic computations on arrays [11]. This pattern<br />
matching syntax provides a partial pattern-match which is applied<br />
at the current context of a cursored array. An example for the onedimensional<br />
Laplace operator is:<br />
laplace1D | x @y z | = x − 2 ∗ y + z<br />
Here the pattern is delimetered by pipes | and consists of three<br />
variable patterns where y is bound to the value at the current<br />
context (cursor) i for the array, denoted by the @ symbol, x is<br />
bound to the value at i − 1 and z is bound to i + 1. Thus the lexical<br />
position of the remaining patterns, relative to the cursor pattern,<br />
determines to which context they are bound. There can be only one<br />
@ symbol for the whole pattern match.<br />
This pattern matching syntax is used to provide static information<br />
of the data access pattern on an array which is used in our DSL<br />
to enforce safety properties on arrays at compile time and eliminate<br />
run-time bounds checking [10].<br />
A similar syntax could be imagined for other comonads. For example<br />
streams could have a pattern syntax with @ for the current<br />
position and with the ability to differentiate between the first position<br />
in a stream and a position at any other point within the stream<br />
e.g. followed by could be defined:<br />
fby 〈@(x, y) | = x<br />
fby | (x, y) @ | = y<br />
In the first clause 〈 denotes the start of a stream thus at context 0<br />
the first parameter is returned. The second clause matches context<br />
n, returning the second parameter at context n−1 (where n >= 1).<br />
We call this syntax context matching as it defines partial<br />
matches focussed at the current context indicated by @. We speculate<br />
that this syntax could be generalised to other comonads and<br />
used to simplify definitions of comonadic operations.<br />
9. Concluding remarks<br />
Whilst monads are ubiquitous, comonads are still relatively underused<br />
and less-widely accepted. A possible reason is that there are<br />
fewer known useful comonads than of monads.<br />
This asymmetry may be due to theoretical reasons. For example,<br />
whilst there are many simple examples of monoids in mathematics<br />
(+, ×, max, ∪, etc.) there are very few simple comonoids, in fact,<br />
the trivial diagonal comonoid which duplicates its parameter appears<br />
to be the only simple example. Since monads and comonads<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 11 2012/9/22
are respectively instances of monoids and comonoids in the category<br />
of endofunctors the asymmetry between their examples may<br />
have some effect at this higher-level. Whilst, monads have a productive<br />
and generative flavour, creating and combining structured<br />
data, comonads have a consuming or observational flavour which<br />
is restricted. The shape preservation property (Section 4.1) shows<br />
the restrictive nature of the comonad laws: an operation may only<br />
observe a structure and transform the elements, but its shape must<br />
be preserved. Compared with the ability of monads to create and<br />
combine structure, comonads seem relatively constrained.<br />
However, despite these reasons there are many useful comonads<br />
of which this paper has shown several, summarised in Figure 3.<br />
There are many more comonads which have not been shown including<br />
examples on other recursive data types such as comonads<br />
of trees [16] or graphs. The examples shown in this paper also have<br />
variations. For example, the non-empty list comonad operated on<br />
suffixes of a list but an alternate list comonad could provide operations<br />
over the entire list with a cursor indicating the current element.<br />
The cursor could be encoded as an address i.e. an integer<br />
value of the current list position, or structurally by splitting the list<br />
into the past, current, and future elements i.e. C a = [a ] a [a ].<br />
It should be noted that many data types which are comonads<br />
are instances of the general concept of containers [1]. Containers<br />
have a set of shapes S, and for a particular shape s ∈ S there<br />
�is<br />
a set of positions P s. The general container data type is Fa =<br />
s:S<br />
(P s → a) i.e. a coproduct of maps from a set of positions to<br />
values, for each possible shape. Ahman et. al recently showed that<br />
this general formulation of containers is a comonad [2].<br />
A shape preserving morphism on a container with a single shape<br />
s is thus a morphism (P s → a) → (P s → b), which is equivalent<br />
to a comonadic operation (P s → a) × P s → b) i.e. an operation<br />
of the InContext comonad for P s contexts (the set of positions<br />
for the s-shaped container). Lists, streams, trees, arrays, and graphs<br />
fit within the container description – however using the InContext<br />
comonad for these comonads may not be practical. For example the<br />
Array data type for the array comonad provides a more efficient<br />
implementation which is more easily mapped to hardware.<br />
Whilst the <strong>codo</strong>-<strong>notation</strong> was developed here in Haskell, it<br />
could be applied in other languages with further benefits. For example,<br />
a <strong>codo</strong>-<strong>notation</strong> for ML could be used to abstract laziness using<br />
a delayed-computation comonad with data type C a = () → a, or<br />
defining lazy lists using the stream comonad shown here.<br />
The simple and natural <strong>notation</strong> provided by <strong>codo</strong> presented<br />
here considerably simplifies programming with comonads. It is our<br />
hope that this prompts the use of comonads as a design pattern<br />
and tool for abstraction and that it promotes further exploration of<br />
comonads yielding new and interesting examples.<br />
Acknowledgements<br />
Thanks are due to Jeremy Gibbons, Tomas Petricek, Tarmo Uustalu,<br />
and Varmo Vene for helpful discussions. This research was supported<br />
by an EPSRC Doctoral Training Award.<br />
References<br />
[1] M. Abbott, T. Altenkirch, and N. Ghani. Containers: constructing<br />
strictly positive types. Theoretical Computer Science, 342(1):3–27,<br />
2005.<br />
[2] D. Ahman, J. Chapman, and T. Uustalu. When is a container a<br />
comonad? Foundations of Software Science and Computational Structures,<br />
pages 74–88, 2012.<br />
[3] E. A. Ashcroft, A. A. Faustini, R. Jagannathan, and W. W. Wadge.<br />
Multidimensional programming. Oxford University Press, Oxford,<br />
UK, 1995.<br />
[4] S. Capobianco and T. Uustalu. A Categorical Outlook on Cellular<br />
Automata. volume 13, pages 88–99, 2010.<br />
[5] J. Hughes. Programming with arrows. Advanced Functional Programming,<br />
pages 73–129, 2005.<br />
[6] Richard B. Kieburtz. Codata and Comonads in Haskell, 1999.<br />
[7] J. Lambek and P.J. Scott. Introduction to higher-order categorical<br />
logic. Cambridge Univ Pr, 1988.<br />
[8] Eugenio Moggi. Computational lambda-calculus and monads. In<br />
Logic in Computer Science, 1989. LICS’89, pages 14–23. IEEE, 1989.<br />
[9] Eugenio Moggi. Notions of computation and monads. Inf. Comput.,<br />
93(1):55–92, 1991.<br />
[10] D. Orchard and A. Mycroft. Efficient and Correct Stencil Computation<br />
via Pattern Matching and Static Typing. Electronic Proceedings in<br />
Theoretical Computer Science, 66:68–92, 2011.<br />
[11] Dominic Orchard, Max Bolingbroke, and Alan Mycroft. Ypnos:<br />
declarative, parallel structured grid programming. In DAMP ’10, pages<br />
15–24, NY, USA, 2010. ACM.<br />
[12] R. Paterson. A new <strong>notation</strong> for arrows. In ACM SIGPLAN Notices,<br />
volume 36, pages 229–240. ACM, 2001.<br />
[13] Tomas Petricek and Don Syme. Syntax Matters: writing abstract<br />
computations in F#. Pre-proceedings of TFP, 2012.<br />
[14] Dan Piponi. Evaluating cellular automata is comonadic, December<br />
2006, Last retreived September 2009. http://blog.sigfpe.com/<br />
2006/12/evaluating-cellular-automata-is.html.<br />
[15] John Plaice. Cartesian Programming. University of Grenoble, France,<br />
2010. HDR (Habilitation diriger des recherches).<br />
[16] Tarmo Uustalu and Varmo Vene. Comonadic functional attribute<br />
evaluation. Trends in Functional Programming-Volume 6, pages 145–<br />
160, 2007.<br />
[17] Tarmo Uustalu and Varmo Vene. Comonadic Notions of Computation.<br />
Electron. Notes Theor. Comput. Sci., 203(5):263–284, 2008.<br />
[18] Tarmo Uustalu and Varmo Vene. The Essence of Dataflow Programming.<br />
Lecture Notes in Computer Science, 4164:135–167, Nov 2006.<br />
[19] William W. Wadge and Edward A. Ashcroft. LUCID, the dataflow programming<br />
language. Academic Press Professional, Inc., San Diego,<br />
CA, USA, 1985.<br />
[20] Philip Wadler. The essence of functional programming. In Proceedings<br />
of POPL ’92, pages 1–14. ACM, 1992.<br />
[21] Philip Wadler. Monads for functional programming. Advanced Functional<br />
Programming, pages 24–52, 1995.<br />
A. Proof of shape preservation<br />
A useful additional derived property is that:<br />
cmap g ◦ cobind f = cobind (g ◦ f ) (7)<br />
The proof of which uses the definition of cmap (Section 2.1):<br />
cmap f x = cobind (f ◦ current) x<br />
cmap g ◦ cobind f<br />
≡ cobind (g ◦ current) ◦ cobind f definition of cmap<br />
≡ cobind (g ◦ current ◦ cobind f ) [C3]<br />
≡ cobind (g ◦ f ) � [C2]<br />
Secondly, const has the property that: (const x)◦f = (const x)◦<br />
g. The proof of shape preservation is then:<br />
shape ◦ (cobind f )<br />
= (cmap (const ()) ◦ (cobind f ) definition of shape<br />
= cobind ((const ()) ◦ f ) (7)<br />
= cobind ((const ()) ◦ current) const property<br />
= (cmap (const ())) ◦ (cobind current) (7)<br />
= cmap (const ()) [C2]<br />
= shape � definition of shape<br />
Draft- Long version - Short version appears in pre-proceedings of IFL 2012 - Last updated September 22, 2012 12 2012/9/22