Drop a premise in a goal in apply style - isabelle

let's assume I want to show the following lemma
lemma "⟦ A; B; C ⟧ ⟹ D"
I get the goal
1. A ⟹ B ⟹ C ⟹ D
However, I don't need B. How can I transfer my goal to something like
1. A ⟹ C ⟹ D
I don't want to alter the original lemma statement, just the current goal in apply style.

What you want is apply (thin_tac B). However, the last time I did this, Peter Lammich shouted "Oh god, why are you doing this!" in disgust and rewrote my proof in order to get rid of the thin_tac. So using this tactic doesn't exactly seem to be encouraged anymore.

Normally it is better to avoid unwanted stuff in a goal state, instead of removing it later. The way you formulate a proof problem affects the way you solve it.
This is particularly important for structured proofs: you appeal positively to those facts that should participate in the next step of the proof, instead of suppressing some of them negatively.
E.g. like this:
from `A` and `C` have D ...
Telling which facts are relevant to a proof is already a start for readability.
Following that style, your initial problem will look like this:
lemma
assumes A and B and C
shows D
proof -
from `A` and `C` show D sorry
qed
or like this with reduced verbosity, if A B C D are large propositions:
lemma
assumes a: A and b: B and c: C
shows D
proof -
from a c show ?thesis sorry
qed

Related

How to prove that the Church encoding, forall r. (F r -> r) -> r, gives an initial algebra of the functor F? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
The well-known Church encoding of natural numbers can be generalized to use an arbitrary functor F. The result is the type, call it C, defined by
data C = Cfix { run :: forall r. (F r -> r) -> r }
Here and below, for simplicity, we will assume that F is a fixed, already defined functor.
It is widely known and stated that the type C is a fixpoint of the functor F, and also that C is an initial F-algebra. For example, if the functor F a is defined by
data F a b = Empty | Cons a b
then a fixpoint of F a is [a] (the list of values of type a). Also, [a] is the initial algebra. The Church encoding of lists is well known. But I could not find a rigorous proof of either of these statements (C is a fixpoint, and C is the initial algebra).
The question is, how to prove rigorously one of the two statements:
The type C is a fixpoint of the type isomorphism F C ≅ C. In other words, we need to prove that there exist two functions, fix :: F C -> C and unfix :: C -> F C such that fix . unfix = id and unfix . fix = id.
The type C is the initial algebra of the functor F; that is, the initial object in the category of F-algebras. In other words, for any type A such that a function p :: F A -> A is given (that is, A is an F-algebra), we can find a unique function q :: C -> A which is an F-algebra morphism. This means, q must be such that the law q . fix = p . fmap q holds. We need to prove that, given A and p, such q exists and is unique.
These two statements are not equivalent; but proving (2) implies (1). (Lambek's theorem says that an initial algebra is an isomorphism.)
The code of the functions fix and unfix can be written relatively easily:
fix :: F C -> C
fix fc = Cfix (forall r. \g -> g . fmap (\h -> h g) fc )
unfix :: C -> F C
unfix c = (run c) (fmap fix)
Given a function p :: F A -> A, the code of the function q is written as
q :: C -> A
q c = (run c) p
However, it seems difficult to prove directly that the functions fix, unfix, q satisfy the required properties. I was not able to find a complete proof.
Is it easier to prove that C is an initial algebra, i.e., that q is unique, than to prove that fix . unfix = id?
In the rest of this question, I will show some steps that I was able to make towards the proof that fix . unfix = id.
It is not possible to prove either (1) or (2) simply by using the given code of the functions. We need additional assumptions. Similarly to the Yoneda identity,
forall r. (A -> r) -> F r ≅ F A ,
we need to assume that the functions' code is fully parametric (no side effects, no specially chosen values or fixed types) so that the parametricity theorem can be applied. So, we need to assume that the type C contains only functions of type forall r. (F r -> r) -> r that satisfy the appropriate naturality law.
The parametricity theorem gives the following naturality law for this type signature: for any types A and B, and for any functions p :: F B -> A and f :: A -> B, the function c :: forall r. (F r -> r) -> r must satisfy the equation
c (f . p) = f . c (p . fmap f)
Using this naturality law with appropriately chosen p and f, one can show that the composition fix . unfix is a certain function of type C -> C that must be equal to \c -> (run c) fix.
However, further progress in the proof does not seem to be possible; it is not clear why this function must be equal to id.
Let us temporarily define the function m:
m :: (F C -> C) -> C -> C
m t c = (run c) t
Then the result I have is written as
fix . unfix = m fix
One can also show that unfix . fix = fmap (m fix).
It remains to prove that m fix = id. Once that is proved, we will have proved that F C ≅ C.
The same naturality law of c with different choice of p and f gives the strange identity
m fix . m (m fix . fix) = m (m fix . fix)
But I do not know how to derive from this identity that m fix = id.

Conditional definition in Isabelle

Assume you want to formalize the following definition in Isabelle:
add a b =
if f(a,b) ≠ 0 then add_1 a b
if g(a,b) ≠ 0 then add_2 a b
where I do not want to give any preference to any option. In fact, the next properties I need to prove are:
add assigns at least one value for any input
add is well-defined
what should I use to model this definition in Isabelle? Perhaps a partial function?
You can see equation (17) and lemmas 4.3.4, 4.3.4 on this paper to see what lemmas I am talking about.
You can use the function command with overlapping equations and preconditions:
function add :: "..." where
"add a b = add_1 a b" if "f a b ≠ 0"
| "add a b = add_2 a b" if "g a b ≠ 0"
apply(atomize_elim) (* makes the exhaustiveness goal more readable *)
This gives you a proof obligations that the cases are really exhaustive and that the right hand sides denode the same when they overlap. Once you have shown this, you then must show termination (if your function is not recursive, then this is done trivially by
termination by lexicographic_order
If your equations are actually not exhaustive, then you first have to add more cases. In this example, you could add
| "add a b = undefined a b" if "f a b = 0 & g a b = 0"
which says that if both f and g are 0, then add a b shall denote some unspecified value and this value may be different for every such choice of a and b.

Difference in implementation of gcd between logic and functional programming

I'm currently learning programming language concepts and pragmatics, hence I feel like I need help in differentiating two subbranches of declarative language family.
Consider the following code snippets which are written in Scheme and Prolog, respectively:
;Scheme
(define gcd
(lambda (a b)
(cond ((= a b) a)
((> a b) (gcd (- a b) b))
(else (gcd (- b a) a)))))
%Prolog
gcd(A, B, G) :- A = B, G = A.
gcd(A, B, G) :- A > B, C is A-B, gcd(C, B, G).
gcd(A, B, G) :- B > A, C is B-A, gcd(C, A, G).
The thing that I didn't understand is:
How do these two different programming languages behave
differently?
Where do we make the difference so that they are categorized either
Functional or Logic-based programming language?
As far as I'm concerned, they do exactly the same thing, calling recursive functions until it terminates.
Since you are using very low-level predicates in your logic programming version, you cannot easily see the increased generality that logic programming gives you over functional programming.
Consider this slightly edited version of your code, which uses CLP(FD) constraints for declarative integer arithmetic instead of the low-level arithmetic you are currently using:
gcd(A, A, A).
gcd(A, B, G) :- A #> B, C #= A - B, gcd(C, B, G).
gcd(A, B, G) :- B #> A, C #= B - A, gcd(C, A, G).
Importantly, we can use this as a true relation, which makes sense in all directions.
For example, we can ask:
Are there two integers X and Y such that their GCD is 3?
That is, we can use this relation in the other direction too! Not only can we, given two integers, compute their GCD. No! We can also ask, using the same program:
?- gcd(X, Y, 3).
X = Y, Y = 3 ;
X = 6,
Y = 3 ;
X = 9,
Y = 3 ;
X = 12,
Y = 3 ;
etc.
We can also post even more general queries and still obtain answers:
?- gcd(X, Y, Z).
X = Y, Y = Z ;
Y = Z,
Z#=>X+ -1,
2*Z#=X ;
Y = Z,
_1712+Z#=X,
Z#=>X+ -1,
Z#=>_1712+ -1,
2*Z#=_1712 ;
etc.
That's a true relation, which is more general than a function of two arguments!
See clpfd for more information.
The GCD example only lightly touches on the differences between logic programming and functional programming as they are much closer to each other than to imperative programming. I will concentrate on Prolog and OCaml, but I believe it is quite representative.
Logical Variables and Unification:
Prolog allows to express partial datastructures e.g. in the term node(24,Left,Right) we don't need to specify what Left and Right stand for, they might be any term. A functional language might insert a lazy function or a thunk which is evaluated later on, but at the creation of the term, we need to know what to insert.
Logical variables can also be unified (i.e. made equal). A search function in OCaml might look like:
let rec find v = function
| [] -> false
| x::_ when v = x -> true
| _::xs (* otherwise *) -> find v xs
While the Prolog implementation can use unification instead of v=x:
member_of(X,[X|_]).
member_of(X,[_|Xs]) :-
member_of(X,Xs).
For the sake of simplicity, the Prolog version has some drawbacks (see below in backtracking).
Backtracking:
Prolog's strength lies in successively instantiating variables which can be easily undone. If you try the above program with variables, Prolog will return you all possible values for them:
?- member_of(X,[1,2,3,1]).
X = 1 ;
X = 2 ;
X = 3 ;
X = 1 ;
false.
This is particularly handy when you need to explore search trees but it comes at a price. If we did not specify the size of the list, we will successively create all lists fulfilling our property - in this case infinitely many:
?- member_of(X,Xs).
Xs = [X|_3836] ;
Xs = [_3834, X|_3842] ;
Xs = [_3834, _3840, X|_3848] ;
Xs = [_3834, _3840, _3846, X|_3854] ;
Xs = [_3834, _3840, _3846, _3852, X|_3860] ;
Xs = [_3834, _3840, _3846, _3852, _3858, X|_3866] ;
Xs = [_3834, _3840, _3846, _3852, _3858, _3864, X|_3872]
[etc etc etc]
This means that you need to be more careful using Prolog, because termination is harder to control. In particular, the old-style ways (the cut operator !) to do that are pretty hard to use correctly and there's still some discussion about the merits of recent approaches (deferring goals (with e.g. dif), constraint arithmetic or a reified if). In a functional programming language, backtracking is usually implemented by using a stack or a backtracking state monad.
Invertible Programs:
Perhaps one more appetizer for using Prolog: functional programming has a direction of evaluation. We can use the find function only to check if some v is a member of a list, but we can not ask which lists fulfill this. In Prolog, this is possible:
?- Xs = [A,B,C], member_of(1,Xs).
Xs = [1, B, C],
A = 1 ;
Xs = [A, 1, C],
B = 1 ;
Xs = [A, B, 1],
C = 1 ;
false.
These are exactly the lists with three elements which contain (at least) one element 1. Unfortunately the standard arithmetic predicates are not invertible and together with the fact that the GCD of two numbers is always unique is the reason why you could not find too much of a difference between functional and logic programming.
To summarize: logic programming has variables which allow for easier pattern matching, invertibility and exploring multiple solutions of the search tree. This comes at the cost of complicated flow control. Depending on the problem it is easier to have a backtracking execution which is sometimes restricted or to add backtracking to a functional language.
The difference is not very clear from one example. Programming language are categorized to logic,functional,... based on some characteristics that they support and as a result they are designed in order to be more easy for programmers in each field (logic,functional...). As an example imperative programming languages (like c) are very different from object oriented (like java,C++) and here the differences are more obvious.
More specifically, in your question the Prolog programming language has adopted he philosophy of logic programming and this is obvious for someone who knows a little bit about mathematical logic. Prolog has predicates (rather than functions-basically almost the same) which return true or false based on the "world" we have defined which is for example what facts and clauses do we have already defined, what mathematical facts are defined and more....All these things are inherited by mathematical logic (propositional and first order logic). So we could say that Prolog is used as a model to logic which makes logical problems (like games,puzzles...) more easy to solve. Moreover Prolog has some features that general-purpose languages have. For example you could write a program in your example to calculate gcd:
gcd(A, B, G) :- A = B, G = A.
gcd(A, B, G) :- A > B, C is A-B, gcd(C, B, G).
gcd(A, B, G) :- B > A, C is B-A, gcd(C, A, G).
In your program you use a predicate gcd in returns TRUE if G unifies with GCD of A,B, and you use multiple clauses to match all cases. When you query gcd(2,5,1). will return True (NOTE that in other languages like shceme you can't give the result as parameter), while if you query gcd(2,5,G). it unifies G with gcd of A,B and returns 1, it is like asking Prolog what should be G in order gcd(2,5,G). be true. So you can understand that it is all about when the predicate succeeds and for that reason you can have more than one solutions, while in functional programming languages you can't.
Functional languages are based in functions so always return the SAME
TYPE of result. This doesn't stand always in Prolog you could have a predicate predicate_example(Number,List). and query predicate_example(5,List). which returns List=... (a list) and also query
predicate_example(Number,[1,2,3]). and return N=... (a number).
The result should be unique, In mathematics, a function is a relation
between a set of inputs and a set of permissible outputs with the property that each input is related to exactly one output
Should be clear what parameter is the variable that will be returned
for example gcd function is of type : N * N -> R so gets A,B parameters which belong to N (natural numbers) and returns gcd. But prolog (with some changes in your program) could return the parameter A,so querying gcd(A,5,1). would give all possible A such that predicate gcd succeeds,A=1,2,3,4,5 .
Prolog in order to find gcd tries every possible way with choice
points so in every step it will try all of you three clauses and will
find every possible solutions. Functional programming languages on
the other hand, like functions should have well unique defined steps
to find the solution.
So you can understand that the difference between Functional and logic languages may not be always visible but they are based on different philosophy-way of thinking.
Imagine how hard would be to solve tic-tac-toe or N queens problem or man-goat-wolf-cabbage problem in Scheme.

What exactly is a pumping lemma and how do you do one?

So I have a pumping lemma question A{www|w ∈ {a,b}*}
I have the correct answer but I'm not fully sure how it works. I'll give the answer just so people know what I'm going with
Assume A is REG
let p be the pumping length
x ∈ A, x=a^p b, a^p b, a^p b.... |s|=3p+3
where each a^p b is a w
Let s = xyz a split such that
1)sum of i>=0 s'=xy'z ∈ A
2)|x|>0 , 3)|xy| <=p
By (3) y contains only a's and by (2) y contains at least 1 a.
Let s'=xyyz, Then s=a^+ ba^p ba^p b,
1)s' ∈ A as it contains contradiction t>p
ie. A not an element of REG
I do think you'd get better answers on cs.stackexchange, but here's the basic overview:
The pumping lemma (for regular languages; there's a more complicated one for context free languages) is a result about regular languages that says "if L is a regular language, then there's an integer p so that every word in L that's at least as long as p can be divided into three parts x, y, and z so that xz ∈ L, xyz ∈ L, xyyz ∈ L, xyyyz ∈ L, etc." (There are a few more details, like the length of y is at least 1 and the length of xy is <= p, so check Wikipedia for the formal statement) "p" is often called the "pumping length" of the language.
This result is usually used as a way to prove that a language is not regular - these proofs work by saying something like:
If L were regular, it would have a pumping length p as in the pumping lemma.
Construct a string that's longer than p letters that's in the language. Usually this string will end up being much longer than p letters long, but will have some part at the beginning that is p letters long to make the next step easier.
Show that when you "pump" this string (that is, when you repeat the part "y" some number of times) you get stuff that isn't in the language L.
Therefore, the pumping lemma doesn't hold for L and so L is not regular.
Note that you can't use this backwards to prove that a language is regular! You can only use this to prove that a language isn't regular, and very occasionally it can be used to prove that a regular language contains certain kinds of strings.
The example proof is following that format. Here's another proof, taken from a recent stackoverflow question:
L = {w ∈ {0,1}* | w has an odd length and the middle character is 0}
Now, proving that L is not regular:
If L were regular, it would have a pumping length p.
Consider the string s = '1'*p + '0' + '1'*p - that string is in L and longer than p characters. Therefore, by the pumping lemma s can be divided into three parts x, y, z such that |xy|<=p, |y|>0, and strings like xyyyz are in L.
But because of how s was constructed, we know that part y only contains '1' characters, do the string xyyyz only has a single '0' character, and it has more '1' characters to the left of the '0' than to the right, so xyyyz isn't in L.
Therefore, L is not regular.

So: what's the point?

What is the intended purpose of the So type? Transliterating into Agda:
data So : Bool → Set where
oh : So true
So lifts a Boolean proposition up to a logical one. Oury and Swierstra's introductory paper The Power of Pi gives an example of a relational algebra indexed by the tables' columns. Taking the product of two tables requires that they have different columns, for which they use So:
Schema = List (String × U) -- U is the universe of SQL types
-- false iff the schemas share any column names
disjoint : Schema -> Schema -> Bool
disjoint = ...
data RA : Schema → Set where
-- ...
Product : ∀ {s s'} → {So (disjoint s s')} → RA s → RA s' → RA (append s s')
I'm used to constructing evidence terms for the things I want to prove about my programs. It seems more natural to construct a logical relation on Schemas to ensure disjointedness:
Disjoint : Rel Schema _
Disjoint s s' = All (λ x -> x ∉ cols s) (cols s')
where cols = map proj₁
So seems to have serious disadvantages compared to a "proper" proof-term: pattern matching on oh doesn't give you any information with which you could make another term type-check (Does it?) - which would mean So values can't usefully participate in interactive proving. Contrast this with the computational usefulness of Disjoint, which is represented as a list of proofs that each column in s' doesn't appear in s.
I don't really believe that the specification So (disjoint s s') is simpler to write than Disjoint s s' - you have to define the Boolean disjoint function without help from the type-checker - and in any case Disjoint pays for itself when you want to manipulate the evidence contained therein.
I am also sceptical that So saves effort when you're constructing a Product. In order to give a value of So (disjoint s s'), you still have to do enough pattern matching on s and s' to satisfy the type checker that they are in fact disjoint. It seems like a waste to discard the evidence thus generated.
So seems unwieldy for both authors and users of code in which it's deployed. 'So', under what circumstances would I want to use So?
If you already have a b : Bool, you can turn it into proposition: So b, which is a bit shorther than b ≡ true. Sometimes (I don't remember any actual case) there is no need to bother with a proper data type, and this quick solution is enough.
So seems to have serious disadvantages compared to a "proper"
proof-term: pattern matching on oh doesn't give you any information
with which you could make another term type-check. As a corollary,
So values can't usefully participate in interactive proving.
Contrast this with the computational usefulness of Disjoint, which
is represented as a list of proofs that each column in s' doesn't
appear in s.
So does give you the same information as Disjoint — you just need to extract it. Basically, if there is no inconsistency between disjoint and Disjoint, then you should be able to write a function So (disjoint s) -> Disjoint s using pattern matching, recursion and impossible cases elimination.
However, if you tweak the definition a bit:
So : Bool -> Set
So true = ⊤
So false = ⊥
So becomes a really useful data type, because x : So true immediately reduces to tt due to the eta-rule for ⊤. This allows to use So like a constraint: in pseudo-Haskell we could write
forall n. (n <=? 3) => Vec A n
and if n is in canonical form (i.e. suc (suc (suc ... zero))), then n <=? 3 can be checked by the compiler and no proofs are needed. In actual Agda it is
∀ {n} {_ : n <=? 3} -> Vec A n
I used this trick in this answer (it is {_ : False (m ≟ 0)} there). And I guess it would be impossible to write a usable version of the machinery decribed here without this simple definition:
Is-just : ∀ {α} {A : Set α} -> Maybe A -> Set
Is-just = T ∘ isJust
where T is So in the Agda's standard library.
Also, in the presence of instance arguments So-as-a-data-type can be used as So-as-a-constraint:
open import Data.Bool.Base
open import Data.Nat.Base
open import Data.Vec
data So : Bool -> Set where
oh : So true
instance
oh-instance : So true
oh-instance = oh
_<=_ : ℕ -> ℕ -> Bool
0 <= m = true
suc n <= 0 = false
suc n <= suc m = n <= m
vec : ∀ {n} {{_ : So (n <= 3)}} -> Vec ℕ n
vec = replicate 0
ok : Vec ℕ 2
ok = vec
fail : Vec ℕ 4
fail = vec

Resources