[Agda] Agda goes coinductive

Dan Doel dan.doel at gmail.com
Thu Jun 5 22:54:13 CEST 2008


On Thursday 05 June 2008, Ulf Norell wrote:
> One of the code sprints in the recent Agda meeting was to add coinductive
> datatypes to Agda.

Excellent! In celebration, here's everyone's favorite monad: partiality (a mix 
of stuff from Abstract Nonsense for Functional Programmers and General 
Recursion via Coinductive Types).

---- snip ----

data Maybe (a : Set) : Set where
  nothing : Maybe a
  just    : a -> Maybe a

data Nat : Set where
  zero : Nat
  suc  : Nat -> Nat

{-# BUILTIN NATURAL Nat  #-}
{-# BUILTIN ZERO    zero #-}
{-# BUILTIN SUC     suc  #-}

codata D (a : Set) : Set where
  now   : a -> D a
  later : D a -> D a

never : {a : Set} -> D a
never ~ later never

-- return the result of the computation that finishes first
race : {a : Set} -> D a -> D a -> D a
race (now a)    _           ~ now a
race (later da) (now a)     ~ now a
race (later da) (later da') ~ later (race da da')

return : {a : Set} -> a -> D a
return = now

infixl 40 _>>=_
_>>=_ : {a b : Set} -> D a -> (a -> D b) -> D b
(now a)    >>= f ~ f a
(later da) >>= f ~ later (da >>= f)

-- getting something out of the partiality monad
compute : {a : Set} -> Nat -> D a -> Maybe a
compute zero    (now a)    = just a
compute zero    (later da) = nothing
compute (suc n) (now a)    = just a
compute (suc n) (later da) = compute n da

data _↓_ {a : Set} : D a -> a -> Set where
  convergeNow   : {x : a} -> (now x) ↓ x
  convergeLater : {x : a} {dx : D a} -> dx ↓ x -> later dx ↓ x

codata _↑ {a : Set} : D a -> Set where
  diverge : {dx : D a} -> dx ↑ -> later dx ↑

-- problems here
-- diverge-never : never ↑
-- diverge-never ~ diverge diverge-never
-- complaint: never != later never

fix-aux : {a b : Set} -> (a -> D b) -> ((a -> D b) -> a -> D b) -> a -> D b
fix-aux k f a with f k a
...           | now b   ~ now b
...           | later d ~ later (fix-aux (f k) f a)

fix : {a b : Set} -> ((a -> D b) -> a -> D b) -> a -> D b
fix ~ fix-aux (\_ -> never)

---------------------------------------------------------------
-- example: quicksort

data Bool : Set where
  false : Bool
  true  : Bool

not : Bool -> Bool
not true  = false
not false = true

_<=_ : Nat -> Nat -> Bool
zero  <= _     = true
suc x <= zero  = false
suc x <= suc y = x <= y

_>_ : Nat -> Nat -> Bool
x > y = not (x <= y)

infixr 70 _::_
data [_] (a : Set) : Set where
  []   : [ a ]
  _::_ : a -> [ a ] -> [ a ]

filter : {a : Set} -> (a -> Bool) -> [ a ] -> [ a ]
filter _ [] = []
filter p (x :: xs) with p x
...                | false = filter p xs
...                | true  = x :: filter p xs

infixr 60 _++_
_++_ : {a : Set} -> [ a ] -> [ a ] -> [ a ]
[] ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

qsort-aux : ([ Nat ] -> D [ Nat ]) -> [ Nat ] -> D [ Nat ]
qsort-aux qsort [] = return []
qsort-aux qsort (x :: xs) = qsort l >>= \l' ->
                            qsort u >>= \u' ->
                            return (l' ++ x :: u')
 where
 l = filter (\y -> y <= x) xs
 u = filter (\y -> y > x) xs

qsort : [ Nat ] -> D [ Nat ]
qsort = fix qsort-aux

---- snip ----

As noted, the proof of the divergence of never (from General Recursion via 
Coinductive Types) doesn't seem to be liked. Also, I haven't yet figured out 
how to get the result of quicksort evaluated (no matter how large a number I 
pass to compute). I'm not exactly sure what I'm doing wrong.

This simpler example works, though:

---- snip ----

_+_ : Nat -> Nat -> Nat
zero    + n = n
(suc m) + n = suc (m + n)
{-# BUILTIN NATPLUS _+_ #-}

_*_ : Nat -> Nat -> Nat
zero * n    = zero
(suc m) * n = n + (m * n)
{-# BUILTIN NATTIMES _*_ #-}

fac-aux : (Nat -> D Nat) -> Nat -> D Nat
fac-aux _ 0 = return 1
fac-aux fac (suc n) = fac n >>= \fn -> return ((suc n) * fn)

fac : Nat -> D Nat
fac = fix fac-aux

fac' : Nat -> Maybe Nat
fac' n = compute (suc n) (fac n)

---- snip ----

Of course, factorial doesn't need general recursion.

-- Dan Doel


More information about the Agda mailing list