Weak sequences.
While the seq
structure allows for lists which may not be finite,
a weak sequence also allows the computation of each element to
involve an indeterminate amount of computation, including possibly
an infinite loop. This is represented as a regular seq
interspersed
with none
elements to indicate that computation is ongoing.
This model is appropriate for Haskell style lazy lists, and is closed under most interesting computation patterns on infinite lists, but conversely it is difficult to extract elements from it.
Equations
- stream.wseq α = stream.seq (option α)
Instances for stream.wseq
Turn a sequence into a weak sequence
Equations
Instances for stream.wseq.of_seq
Turn a list into a weak sequence
Equations
Turn a stream into a weak sequence
Equations
Equations
Equations
Equations
The empty weak sequence
Equations
Equations
Prepend an element to a weak sequence
Equations
Compute for one tick, without producing any elements
Equations
Destruct a weak sequence, to (eventually possibly) produce either
none
for nil
or some (a, s)
if an element is produced.
Equations
- stream.wseq.destruct = computation.corec (λ (s : stream.wseq α), stream.wseq.destruct._match_1 (stream.seq.destruct s))
- stream.wseq.destruct._match_1 (option.some (option.some a, s')) = sum.inl (option.some (a, s'))
- stream.wseq.destruct._match_1 (option.some (option.none α, s')) = sum.inr s'
- stream.wseq.destruct._match_1 option.none = sum.inl option.none
Recursion principle for weak sequences, compare with list.rec_on
.
Equations
- s.rec_on h1 h2 h3 = stream.seq.rec_on s h1 (λ (o : option α), o.rec_on h3 h2)
membership for weak sequences
Equations
- stream.wseq.mem a s = stream.seq.mem (option.some a) s
Equations
Get the head of a weak sequence. This involves a possibly infinite computation.
Equations
Instances for stream.wseq.head
Encode a computation yielding a weak sequence into additional
think
constructors in a weak sequence
Equations
- stream.wseq.flatten = stream.seq.corec (λ (c : computation (stream.wseq α)), stream.wseq.flatten._match_1 c.destruct)
- stream.wseq.flatten._match_1 (sum.inr c') = option.some (option.none α, c')
- stream.wseq.flatten._match_1 (sum.inl s) = stream.seq.omap return (stream.seq.destruct s)
Get the tail of a weak sequence. This doesn't need a computation
wrapper, unlike head
, because flatten
allows us to hide this
in the construction of the weak sequence itself.
Equations
- s.tail = stream.wseq.flatten ((λ (o : option (α × stream.wseq α)), o.rec_on stream.wseq.nil prod.snd) <$> s.destruct)
Instances for stream.wseq.tail
drop the first n
elements from s
.
Instances for stream.wseq.drop
Get the nth element of s
.
Instances for stream.wseq.nth
Convert s
to a list (if it is finite and completes in finite time).
Equations
- s.to_list = computation.corec (λ (_x : list α × stream.wseq α), stream.wseq.to_list._match_2 _x) (list.nil α, s)
- stream.wseq.to_list._match_2 (l, s) = stream.wseq.to_list._match_1 l (stream.seq.destruct s)
- stream.wseq.to_list._match_1 l (option.some (option.some a, s')) = sum.inr (a :: l, s')
- stream.wseq.to_list._match_1 l (option.some (option.none α, s')) = sum.inr (l, s')
- stream.wseq.to_list._match_1 l option.none = sum.inl l.reverse
Instances for stream.wseq.to_list
Get the length of s
(if it is finite and completes in finite time).
Equations
- s.length = computation.corec (λ (_x : ℕ × stream.wseq α), stream.wseq.length._match_2 _x) (0, s)
- stream.wseq.length._match_2 (n, s) = stream.wseq.length._match_1 n (stream.seq.destruct s)
- stream.wseq.length._match_1 n (option.some (option.some a, s')) = sum.inr (n + 1, s')
- stream.wseq.length._match_1 n (option.some (option.none α, s')) = sum.inr (n, s')
- stream.wseq.length._match_1 n option.none = sum.inl n
- out : s.to_list.terminates
A weak sequence is finite if to_list s
terminates. Equivalently,
it is a finite number of think
and cons
applied to nil
.
Get the list corresponding to a finite weak sequence.
- nth_terminates : ∀ (n : ℕ), (s.nth n).terminates
A weak sequence is productive if it never stalls forever - there are
always a finite number of think
s between cons
constructors.
The sequence itself is allowed to be infinite though.
Instances of this typeclass
Replace the n
th element of s
with a
.
Equations
- s.update_nth n a = stream.seq.corec (λ (_x : ℕ × stream.wseq α), stream.wseq.update_nth._match_2 a _x) (n + 1, s)
- stream.wseq.update_nth._match_2 a (n, s) = stream.wseq.update_nth._match_1 a (stream.seq.destruct s) n
- stream.wseq.update_nth._match_1 a (option.some (option.some a', s')) (n + 2) = option.some (option.some a', n + 1, s')
- stream.wseq.update_nth._match_1 a (option.some (option.some a', s')) 1 = option.some (option.some a, 0, s')
- stream.wseq.update_nth._match_1 a (option.some (option.some a', s')) 0 = option.some (option.some a', 0, s')
- stream.wseq.update_nth._match_1 a (option.some (option.none α, s')) n = option.some (option.none α, n, s')
- stream.wseq.update_nth._match_1 a option.none n = option.none
Remove the n
th element of s
.
Equations
- s.remove_nth n = stream.seq.corec (λ (_x : ℕ × stream.wseq α), stream.wseq.remove_nth._match_2 _x) (n + 1, s)
- stream.wseq.remove_nth._match_2 (n, s) = stream.wseq.remove_nth._match_1 (stream.seq.destruct s) n
- stream.wseq.remove_nth._match_1 (option.some (option.some a', s')) (n + 2) = option.some (option.some a', n + 1, s')
- stream.wseq.remove_nth._match_1 (option.some (option.some a', s')) 1 = option.some (option.none α, 0, s')
- stream.wseq.remove_nth._match_1 (option.some (option.some a', s')) 0 = option.some (option.some a', 0, s')
- stream.wseq.remove_nth._match_1 (option.some (option.none α, s')) n = option.some (option.none α, n, s')
- stream.wseq.remove_nth._match_1 option.none n = option.none
Map the elements of s
over f
, removing any values that yield none
.
Equations
- stream.wseq.filter_map f = stream.seq.corec (λ (s : stream.wseq α), stream.wseq.filter_map._match_1 f (stream.seq.destruct s))
- stream.wseq.filter_map._match_1 f (option.some (option.some a, s')) = option.some (f a, s')
- stream.wseq.filter_map._match_1 f (option.some (option.none α, s')) = option.some (option.none β, s')
- stream.wseq.filter_map._match_1 f option.none = option.none
Select the elements of s
that satisfy p
.
Equations
- stream.wseq.filter p = stream.wseq.filter_map (λ (a : α), ite (p a) (option.some a) option.none)
Get the first element of s
satisfying p
.
Equations
- stream.wseq.find p s = (stream.wseq.filter p s).head
Zip a function over two weak sequences
Equations
- stream.wseq.zip_with f s1 s2 = stream.seq.corec (λ (_x : stream.wseq α × stream.wseq β), stream.wseq.zip_with._match_2 f _x) (s1, s2)
- stream.wseq.zip_with._match_2 f (s1, s2) = stream.wseq.zip_with._match_1 f s1 s2 (stream.seq.destruct s1) (stream.seq.destruct s2)
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.some a1, s1')) (option.some (option.some a2, s2')) = option.some (option.some (f a1 a2), s1', s2')
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.some a1, s1')) (option.some (option.none β, s2')) = option.some (option.none γ, s1, s2')
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.some val, snd)) option.none = option.none
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.none α, s1')) (option.some (option.some a2, s2')) = option.some (option.none γ, s1', s2)
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.none α, s1')) (option.some (option.none β, s2')) = option.some (option.none γ, s1', s2')
- stream.wseq.zip_with._match_1 f s1 s2 (option.some (option.none α, snd)) option.none = option.none
- stream.wseq.zip_with._match_1 f s1 s2 option.none _x = option.none
Zip two weak sequences into a single sequence of pairs
Equations
Get the list of indexes of elements of s
satisfying p
Equations
- stream.wseq.find_indexes p s = stream.wseq.filter_map (λ (_x : α × ℕ), stream.wseq.find_indexes._match_1 p _x) (s.zip ↑stream.nats)
- stream.wseq.find_indexes._match_1 p (a, n) = ite (p a) (option.some n) option.none
Get the index of the first element of s
satisfying p
Equations
- stream.wseq.find_index p s = (λ (o : option ℕ), o.get_or_else 0) <$> (stream.wseq.find_indexes p s).head
Get the index of the first occurrence of a
in s
Equations
Get the indexes of occurrences of a
in s
Equations
union s1 s2
is a weak sequence which interleaves s1
and s2
in
some order (nondeterministically).
Equations
- s1.union s2 = stream.seq.corec (λ (_x : stream.wseq α × stream.wseq α), stream.wseq.union._match_2 _x) (s1, s2)
- stream.wseq.union._match_2 (s1, s2) = stream.wseq.union._match_1 (stream.seq.destruct s1) (stream.seq.destruct s2)
- stream.wseq.union._match_1 (option.some (option.some a1, s1')) (option.some (option.some a2, s2')) = option.some (option.some a1, stream.wseq.cons a2 s1', s2')
- stream.wseq.union._match_1 (option.some (option.some a1, s1')) (option.some (option.none α, s2')) = option.some (option.some a1, s1', s2')
- stream.wseq.union._match_1 (option.some (option.some val, s1')) option.none = option.some (option.some val, s1', stream.wseq.nil α)
- stream.wseq.union._match_1 (option.some (option.none α, s1')) (option.some (option.some a2, s2')) = option.some (option.some a2, s1', s2')
- stream.wseq.union._match_1 (option.some (option.none α, s1')) (option.some (option.none α, s2')) = option.some (option.none α, s1', s2')
- stream.wseq.union._match_1 (option.some (option.none α, s1')) option.none = option.some (option.none α, s1', stream.wseq.nil α)
- stream.wseq.union._match_1 option.none (option.some (a2, s2')) = option.some (a2, stream.wseq.nil α, s2')
- stream.wseq.union._match_1 option.none option.none = option.none
Returns tt
if s
is nil
and ff
if s
has an element
Equations
Calculate one step of computation
Equations
- s.compute = stream.wseq.compute._match_1 s (stream.seq.destruct s)
- stream.wseq.compute._match_1 s (option.some (option.some val, snd)) = s
- stream.wseq.compute._match_1 s (option.some (option.none α, s')) = s'
- stream.wseq.compute._match_1 s option.none = s
Get the first n
elements of a weak sequence
Equations
- s.take n = stream.seq.corec (λ (_x : ℕ × stream.wseq α), stream.wseq.take._match_2 _x) (n, s)
- stream.wseq.take._match_2 (n, s) = stream.wseq.take._match_1 n (stream.seq.destruct s)
- stream.wseq.take._match_1 (m + 1) (option.some (option.some a, s')) = option.some (option.some a, m, s')
- stream.wseq.take._match_1 (m + 1) (option.some (option.none α, s')) = option.some (option.none α, m + 1, s')
- stream.wseq.take._match_1 (m + 1) option.none = option.none
- stream.wseq.take._match_1 0 _x = option.none
Split the sequence at position n
into a finite initial segment
and the weak sequence tail
Equations
- s.split_at n = computation.corec (λ (_x : ℕ × list α × stream.wseq α), stream.wseq.split_at._match_2 _x) (n, list.nil α, s)
- stream.wseq.split_at._match_2 (n, l, s) = stream.wseq.split_at._match_1 n l s n (stream.seq.destruct s)
- stream.wseq.split_at._match_1 n l s (m + 1) (option.some (option.some a, s')) = sum.inr (m, a :: l, s')
- stream.wseq.split_at._match_1 n l s (m + 1) (option.some (option.none α, s')) = sum.inr (n, l, s')
- stream.wseq.split_at._match_1 n l s (m + 1) option.none = sum.inl (l.reverse, s)
- stream.wseq.split_at._match_1 n l s 0 _x = sum.inl (l.reverse, s)
Returns tt
if any element of s
satisfies p
Equations
- s.any p = computation.corec (λ (s : stream.wseq α), stream.wseq.any._match_1 p (stream.seq.destruct s)) s
- stream.wseq.any._match_1 p (option.some (option.some a, s')) = ite ↥(p a) (sum.inl bool.tt) (sum.inr s')
- stream.wseq.any._match_1 p (option.some (option.none α, s')) = sum.inr s'
- stream.wseq.any._match_1 p option.none = sum.inl bool.ff
Returns tt
if every element of s
satisfies p
Equations
- s.all p = computation.corec (λ (s : stream.wseq α), stream.wseq.all._match_1 p (stream.seq.destruct s)) s
- stream.wseq.all._match_1 p (option.some (option.some a, s')) = ite ↥(p a) (sum.inr s') (sum.inl bool.ff)
- stream.wseq.all._match_1 p (option.some (option.none α, s')) = sum.inr s'
- stream.wseq.all._match_1 p option.none = sum.inl bool.tt
Apply a function to the elements of the sequence to produce a sequence
of partial results. (There is no scanr
because this would require
working from the end of the sequence, which may not exist.)
Equations
- stream.wseq.scanl f a s = stream.wseq.cons a (stream.seq.corec (λ (_x : α × stream.wseq β), stream.wseq.scanl._match_2 f _x) (a, s))
- stream.wseq.scanl._match_2 f (a, s) = stream.wseq.scanl._match_1 f a (stream.seq.destruct s)
- stream.wseq.scanl._match_1 f a (option.some (option.some b, s')) = let a' : α := f a b in option.some (option.some a', a', s')
- stream.wseq.scanl._match_1 f a (option.some (option.none β, s')) = option.some (option.none α, a, s')
- stream.wseq.scanl._match_1 f a option.none = option.none
Get the weak sequence of initial segments of the input sequence
Equations
- s.inits = stream.wseq.cons list.nil (stream.seq.corec (λ (_x : dlist α × stream.wseq α), stream.wseq.inits._match_2 _x) (dlist.empty α, s))
- stream.wseq.inits._match_2 (l, s) = stream.wseq.inits._match_1 l (stream.seq.destruct s)
- stream.wseq.inits._match_1 l (option.some (option.some a, s')) = let l' : dlist α := dlist.concat a l in option.some (option.some l'.to_list, l', s')
- stream.wseq.inits._match_1 l (option.some (option.none α, s')) = option.some (option.none (list α), l, s')
- stream.wseq.inits._match_1 l option.none = option.none
Like take, but does not wait for a result. Calculates n
steps of
computation and returns the sequence computed so far
Equations
- s.collect n = list.filter_map id (stream.seq.take n s)
Append two weak sequences. As with seq.append
, this may not use
the second sequence if the first one takes forever to compute
Equations
Map a function over a weak sequence
Equations
Flatten a sequence of weak sequences. (Note that this allows
empty sequences, unlike seq.join
.)
Equations
- S.join = ((λ (o : option (stream.wseq α)), stream.wseq.join._match_1 o) <$> S).join
- stream.wseq.join._match_1 (option.some s) = (option.none α, s)
- stream.wseq.join._match_1 option.none = stream.seq1.ret option.none
Monadic bind operator for weak sequences
Equations
- s.bind f = (stream.wseq.map f s).join
lift a relation to a relation over weak sequences
Equations
- stream.wseq.lift_rel_o R C (option.some (a, s)) (option.some (b, t)) = (R a b ∧ C s t)
- stream.wseq.lift_rel_o R C (option.some (fst, snd)) option.none = false
- stream.wseq.lift_rel_o R C option.none (option.some val) = false
- stream.wseq.lift_rel_o R C option.none option.none = true
Definitino of bisimilarity for weak sequences
Equations
Two weak sequences are lift_rel R
related if they are either both empty,
or they are both nonempty and the heads are R
related and the tails are
lift_rel R
related. (This is a coinductive definition.)
Equations
- stream.wseq.lift_rel R s t = ∃ (C : stream.wseq α → stream.wseq β → Prop), C s t ∧ ∀ {s : stream.wseq α} {t : stream.wseq β}, C s t → computation.lift_rel (stream.wseq.lift_rel_o R C) s.destruct t.destruct
If two sequences are equivalent, then they have the same values and
the same computational behavior (i.e. if one loops forever then so does
the other), although they may differ in the number of think
s needed to
arrive at the answer.
Equations
auxilary defintion of tail over weak sequences
Equations
auxilary defintion of drop over weak sequences
Equations
- stream.wseq.drop.aux (n + 1) = λ (a : option (α × stream.wseq α)), stream.wseq.tail.aux a >>= stream.wseq.drop.aux n
- stream.wseq.drop.aux 0 = computation.return
Given a productive weak sequence, we can collapse all the think
s to
produce a sequence.
The monadic return a
is a singleton list containing a
.
Equations
auxilary defintion of destruct_append
over weak sequences
Equations
- stream.wseq.destruct_append.aux t (option.some (a, s)) = computation.return (option.some (a, s.append t))
- stream.wseq.destruct_append.aux t option.none = t.destruct
auxilary defintion of destruct_join
over weak sequences