18.11.2012 Views

codo-notation-orchard12

codo-notation-orchard12

codo-notation-orchard12

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!