corecursive Agda functions without sized types - agda

I've been doing some experiments with the "old" and "new" ways of dealing with coinduction in Agda, but I think I'm still missing some important facts about the behavior of Agda's termination checking of corecursive functions when Size is not used.
As an example, consider the following definitions of Stream and repeat:
data Stream (A : Set) : Set
record ∞Stream (A : Set) : Set where
coinductive
field force : Stream A
open ∞Stream
data Stream A where
cons : A -> ∞Stream A -> Stream A
repeat : ∀{A} -> A -> ∞Stream A
force (repeat x) = cons x (repeat x)
Roughly speaking, the coinductive record type ∞Stream is a specialized Thunk for streams. I've seen similar definitions in quite a few Agda formalizations, although they're usually refined a Size for better precision. Anyway, the above definition of repeat is accepted by Agda without any Size annotations.
The problem is when I try to factor out the ∞Stream record into a Thunk, so that I can reuse Thunk elsewhere for defining other potentially infinite data types:
record Thunk (A : Set) : Set where
coinductive
field force : A
open Thunk
data Stream (A : Set) : Set where
cons : A -> Thunk (Stream A) -> Stream A
repeat : ∀{A} -> A -> Thunk (Stream A)
force (repeat x) = cons x (repeat x)
Note that:
the code for repeat is exactly the same in the two cases,
in each case the recursive call of repeat is found immediately below the constructor cons,
in each case repeat produces a value of a coinductive record type,
and yet Agda accepts the first definition of repeat but rejects the second one (Termination checking failed for the following functions: repeat).
I'm aware that using the sized Thunk in the standard library the second version can be amended so that it is accepted by Agda. Nonetheless, I'd like to understand what makes the first version above so different from the second one that it passes the termination checker. They look basically the same to me.
Thanks!

Related

Paths vs Equivalences in cubical agda for specific computational behavior

I'm working in Cubical agda and trying to build up some common utilities for later proofs. One of these is that for any type A it is the 'same' as the type Σ A (\_ -> Top) where Top is the type with one element. And the issue is how to best expose this 'sameness' from the utility library. I can expose it as an equivalence, a path or an isomorphism, and maybe multiple ones.
I originally exposed it as just a path: top-path : Path A (Σ A (\_ -> Top)) which is built up using glue types from an underlying top-equiv : A ≃ Σ A (\_ -> Top). But when trying to use this in later proofs I found out that transporting along this path did not compute as I expected. My expectation was that it composed with fst to the identity, but in practice I found that it sometimes left in transp and prim^unglue terms. This was not the case if I used a particular concrete type for A, or if I used a similar construction using the equivalence.
Example:
-- Working
compute-top-path-Bool : (a : Bool) -> fst (transport (top-path Bool) a) == a
compute-top-path-Bool a = refl
compute-top-equiv-Any : (a : Bool) -> fst (transport-equiv (top-equiv Bool) a) == a
compute-top-equiv-Any a = refl
-- Broken
compute-top-path-Any : (a : A) -> fst (transport (top-path A) a) == a
compute-top-path-Any a = refl
--
-- Checking test (/Users/endobson/tmp/agda/test.agda).
-- /Users/endobson/tmp/agda/test.agda:104,26-30
-- transp (λ i → A) i0 (fst (prim^unglue a)) != a of type A
-- when checking that the expression refl has type
-- fst (transport (top-path A) a) == a
--
Self contained reproduction: https://gist.github.com/endobson/62605cfc15a92b9111391b459d03b548, and I'm using Agda version 2.6.1.3.
Thus my question is what is best solution for this problem? Am I somehow constructing my paths in a too complicated way and if I made them in a different way then the computational behavior would be better? Or is exposing the equivalence directly from my utility library expected? I find exposing the equivalence 'inelegant' as it seems that paths should be able to do this, but am not against doing so if they are known to be the better tool for this particular use case.
In the agda/cubical library we do tend to export the equivalence (or the iso) along with the path, because of issues like the one you mention.
The reason for those extra transp calls is that transport for Glue has to work in the general case where they might actually be required.
fst (prim^unglue a) should reduce away if you ask for the normal form though.

What is `where .force`?

I've been playing around with the idea of writing programs that run on Streams and properties with them, but I feel that I am stuck even with the simplest of things. When I look at the definition of repeat in Codata/Streams in the standard library, I find a construction that I haven't seen anywhere in Agda: λ where .force →.
Here, an excerpt of a Stream defined with this weird feature:
repeat : ∀ {i} → A → Stream A i
repeat a = a ∷ λ where .force → repeat a
Why does where appear in the middle of the lambda function definition?, and what is the purpose of .force if it is never used?
I might be asking something that is in the documentation, but I can't figure out how to search for it.
Also, is there a place where I can find documentation to use "Codata" and proofs with it? Thanks!
Why does where appear in the middle of the lambda function definition?,
Quoting the docs:
Anonymous pattern matching functions can be defined using one of the
two following syntaxes:
\ { p11 .. p1n -> e1 ; … ; pm1 .. pmn -> em }
\ where p11 .. p1n -> e1 … pm1 .. pmn -> em
So λ where is an anonymous pattern matching function. force is the field of Thunk and .force is a copattern in postfix notation (originally I said nonsense here, but thanks to #Cactus it's now fixed, see his answer).
Also, is there a place where I can find documentation to use "Codata" and proofs with it? Thanks!
Check out these papers
Normalization by Evaluation in the Delay Monad
A Case Study for Coinduction via Copatterns and Sized Types
Equational Reasoning about Formal Languages in Coalgebraic Style
Guarded Recursion in Agda via Sized Types
As one can see in the definition of Thunk, force is the field of the Thunk record type:
record Thunk {ℓ} (F : Size → Set ℓ) (i : Size) : Set ℓ where
coinductive
field force : {j : Size< i} → F j
So in the pattern-matching lambda, .force is not a dot pattern (why would it be? there is nothing prescribing the value of the parameter), but instead is simply syntax for the record field selector force. So the above code is equivalent to making a record with a single field called force with the given value, using copatterns:
repeat a = a :: as
where
force as = repeat a
or, which is actually where the .force syntax comes from, using postfix projection syntax:
repeat a = a :: as
where
as .force = repeat a

What is positivity checking? [duplicate]

This question already has an answer here:
"Strictly positive" in Agda
(1 answer)
Closed 4 years ago.
Apparently, there is some feature in Agda called positivity checking which can apparently keep the system sound even if type-in-type is enabled.
I am curious to know what this is about, but the Agda Manual fails to answer the question, and only explains how to turn it off.
At a lunch table I overheard that this is about polarity in type theory, but that is about all I know. I am failing to find anything online which explains this concept and why it is useful in maintaining soundness. Any intelligible explanation would be appreciated.
First, I have to clear up a misconception: positivity checking does not guarantee soundness when type-in-type is enabled. Data types must thus satisfy both the positivity check and universe check to preserve soundness.
Now, to explain positivity checking, let's first look at a counterexample when we wouldn't have positivity checking:
-- the empty type
data ⊥ : Set where
-- a non-positive type
data Bad : Set where
bad : (Bad → ⊥) → Bad
Suppose this datatype was allowed, then you could easily prove ⊥:
bad-is-false : Bad → ⊥
bad-is-false (bad f) = f (bad f)
bad-is-true : Bad
bad-is-true = bad bad-is-false
boom : ⊥
boom = bad-is-false bad-is-true
Under the Curry-Howard correspondence, the definition of Bad says: Bad is true if and only if Bad is false. So it is not surprising that it leads to inconsistencies.
Positivity checking rules out datatypes such as Bad. In general, the (strict) positivity criterion says that each constructor c of a datatype D should have a type of the form
c : (x1 : A1)(x2 : A2) ... (xn : An) → D xs
where the type Ai of each argument is either non-recursive (i.e. it doesn't refer to D) or of the form (y1 : B1)(y2 : B2) ... (ym : Bm) → D ys where each Bj doesn't refer to D.
Bad doesn't satisfy this criterion because the argument of the constructor bad has type Bad → ⊥, which is neither of the two allowed forms.
The name 'positivity checking' comes (as many things in type theory do) from category theory, specifically the notion of a positive endofunctor. Each definition of a datatype that satisfies the positivity criterion is such a positive endofunctor on the category of types. This means we can construct the initial algebra of that endofunctor, which can be used to model the datatype when constructing a model of type theory (which is used to prove soundness).

Is there a name for this higher order function?

I very frequently want to apply the same argument twice to a binary function f, is there a name for this convert function/combinator?
// convert: f: ('a -> 'a -> 'b) -> 'a -> 'b
let convert f x = f x x
Example usage might be partially applying convert with the multiplication operator * to fix the multiplicand and multiplier:
let fixedMultiplication = convert (*)
fixedMultiplication 2 // returns 4
That combinator is usually called a warbler; the name comes from Raymond Smullyan's book To Mock a Mockingbird, which has a bunch of logic puzzles around combinator functions, presented in the form of birds that can imitate each other's songs. See this usage in Suave, and this page which lists a whole bunch of combinator functions (the "standard" ones and some less-well-known ones as well), and the names that Smullyan gave them in his book.
Not really an answer to what it's called in F#, but in APL or J, it's called the "reflexive" (or perhaps "reflex") operator. In APL it is spelt ⍨ and used monadically – i.e. applied to one function (on its left). In J it's called ~, and used in the same way.
For example: f⍨ x is equivalent to x f x (in APL, functions that take two arguments are always used in a binary infix fashion).
So the "fixedMultiplication" (or square) function is ×⍨ in APL, or *~ in J.
This is the monadic join operator for functions. join has type
Monad m => m (m a) => m a
and functions form a monad where the input type is fixed (i.e. ((->) a), so join has type:
(a -> (a -> b)) -> (a -> b)

Coq: typeclasses vs dependent records

I can't understand the difference between typeclasses and dependent records in Coq. The reference manual gives the syntax of typeclasses, but says nothing about what they really are and how should you use them. A bit of thinking and searching reveals that typeclasses essentially are dependent records with a bit of syntactic sugar that allows Coq to automatically infer some implicit instances and parameters. It seems that the algorithm for typeclasses works better when there is more or a less only one possible instance of it in any given context, but that's not a big issue since we can always move all fields of typeclass to its parameters, removing ambiguity. Also the Instance declaration is automatically added to the Hints database which can often ease the proofs but will also sometimes break them, if the instances were too general and caused proof search loops or explosions. Are there any other issues I should be aware of? What is the heuristic for choosing between the two? E.g. would I lose anything if I use only records and set their instances as implicit parameters whenever possible?
You are right: type classes in Coq are just records with special plumbing and inference (there's also the special case of single-method type classes, but it doesn't really affect this answer in any way). Therefore, the only reason you would choose type classes over "pure" dependent records is to benefit from the special inference that you get with them: inference with plain dependent records is not very powerful and doesn't allow you to omit much information.
As an example, consider the following code, which defines a monoid type class, instantiating it with natural numbers:
Class monoid A := Monoid {
op : A -> A -> A;
id : A;
opA : forall x y z, op x (op y z) = op (op x y) z;
idL : forall x, op id x = x;
idR : forall x, op x id = x
}.
Require Import Arith.
Instance nat_plus_monoid : monoid nat := {|
op := plus;
id := 0;
opA := plus_assoc;
idL := plus_O_n;
idR := fun n => eq_sym (plus_n_O n)
|}.
Using type class inference, we can use any definitions that work for any monoid directly with nat, without supplying the type class argument, e.g.
Definition times_3 (n : nat) := op n (op n n).
However, if you make the above definition into a regular record by replacing Class and Instance by Record and Definition, the same definition fails:
Toplevel input, characters 38-39: Error: In environment n : nat The term "n" has type "nat" while it is expected to have type "monoid ?11".
The only caveat with type classes is that the instance inference engine gets a bit lost sometimes, causing hard-to-understand error messages to appear. That being said, it's not really a disadvantage over dependent records, given that this possibility isn't even available there.

Resources