open import Prelude
open import Algebra
open import Algebra.Monus
module Codata.Segments
{ℓ} {𝑆 : Type ℓ}
(mon : CTMAPOM 𝑆)
where
open CTMAPOM mon
private variable i j : 𝑆
-- This is a type which contains some finite and some infinite lists.
-- The idea is that each entry contains a parameter (w) which says
-- how much coinductive "fuel" it uses.
-- The Colist′ A i type represents a colist which is defined down to depth
-- i; the Colist A type represents a "true" colist, i.e. a colist defined for
-- any given depth.
infixr 5 _◃_
data Colist′ {a} (A : Type a) (i : 𝑆) : Type (a ℓ⊔ ℓ) where
_◃_ : ∀ w → -- Segment size
( (w<i : w < i) → -- If there is enough fuel left (i is the fuel)
-- (also the _<_ type is a proposition)
A × Colist′ A (i ∸ w) -- Produce an element followed by the rest of
-- the list, with w taken out of the fuel.
) →
Colist′ A i
Colist : Type a → Type (a ℓ⊔ ℓ)
Colist A = ∀ {i} → Colist′ A i
-- The main interesting things tyhis type can do are the following:
-- * Infinite lists.
-- * The "fuel" parameter can be an arbitrary monoid, not just ℕ
-- * Finite lists can also be specified, and the way we say something is
-- finite is by taking no fuel.
-- * Everything seems to correspond correctly to the monus axioms.
--------------------------------------------------------------------------------
-- Finite colists
--------------------------------------------------------------------------------
-- By adding a finite prefix you don't have to use any of the fuel.
_∹_ : A → Colist A → Colist A
x ∹ xs = ε ◃ λ _ → x , xs
--------------------------------------------------------------------------------
-- Empty colists
--------------------------------------------------------------------------------
-- To terminate computation you use all the fuel, making an empty list.
-- (I'm not sure how principled this is: semantically I don't know if I like
-- that the size of a segment can depend on the supplied size parameter).
empty : Colist A
empty {i = i} = i ◃ λ i<i → ⊥-elim (irrefl i<i)
--------------------------------------------------------------------------------
-- Finite derived colists
--------------------------------------------------------------------------------
-- singleton
pure : A → Colist A
pure x = x ∹ empty
replicate : ℕ → A → Colist A
replicate zero x = empty
replicate (suc n) x = x ∹ replicate n x
--------------------------------------------------------------------------------
-- Infinite colists
--------------------------------------------------------------------------------
-- This unfold function produces an infinite list; it needs every size segment
-- be non empty so that each step uses some fuel. This is what provides the
-- termination argument.
module _
(B : 𝑆 → Type b) -- The seed type
(ϕ : ∀ {i} → -- At depth i
B i → -- With this seed
∃ w × -- Produce a segment of size w
(w ≢ ε) × -- w can't be ε, so that we use some of the fuel to prove
-- termination
((w<i : w < i) → A × B (i ∸ w)) -- And produce the cons constructor.
)
-- ^ The step function
where
unfold′ : Acc _<_ i → B i → Colist′ A i
unfold′ a = uncurry _◃_
∘ map₂
(λ { (w≢ε , xs′) w<i →
map₂ (case a of
λ { (acc wf) →
unfold′ (wf _ (∸‿<-< _ _ w<i w≢ε)) })
(xs′ w<i) })
∘ ϕ
unfold : (fdc : WellFounded _<_)
(B : 𝑆 → Type b)
(ϕ : ∀ {i} → B i → ∃ w × (w ≢ ε) × ((w<i : w < i) → A × B (i ∸ w))) →
(∀ {i} → B i) → Colist A
unfold fdc B ϕ xs {i} = unfold′ B ϕ (fdc i) xs
-- Here's a simple example using the unfold function: this produces infinitely
-- repeated values, with segment size s.
repeat : (fdc : WellFounded _<_) (s : 𝑆) (s≢ε : s ≢ ε) (x : A) → Colist A
repeat fdc s s≢ε x = unfold fdc (const ⊤) (λ _ → s , s≢ε , const (x , tt)) tt
--------------------------------------------------------------------------------
-- Manipulating colists
--------------------------------------------------------------------------------
-- One important thing to note about the Colist type: it is inductive!
-- Although it does technically represent "coinduction", the constructors and
-- type itself are inductive as far as Agda can see. For that reason functions
-- like map pass the termination checker with no extra ceremony.
map : (A → B) → Colist′ A i → Colist′ B i
map f (w ◃ xs) = w ◃ λ w<i → case xs w<i of λ { (y , ys) → f y , map f ys }
-- You can extract a finite prefix of the colist.
open import Data.List using (List; _∷_; [])
take′ : ∀ i → Colist′ A i → List A
take′ i (w ◃ xs) with w <? i
... | no _ = []
... | yes w<i with xs w<i
... | y , ys = y ∷ take′ _ ys
take : 𝑆 → Colist A → List A
take x xs = take′ x xs