Hi Yong
Yong Luo wrote:
Hi, Conor,
Since you use elimination rule on both arguments, I was expecting four cases
like Thorsten did in the previous email. Can we define the Ackermann
function like the following form?
ack x y = Elim_Nat ......
For example, plus can be defined as:
plus x y = Elim_Nat (\n->Nat) x (\m,n->(S n)) y
That's exactly what's going on behind the scenes. Tell you what...
...the attached file now contains three definitions of the same
function. The first is as before. The second, ackFun, is the term you
asked for: we only get three cases, because the second elim only happens
in the suc case of the first. I've checked that all the laws hold
computationally by proving them with refl. The third version, acktual,
shows how Epigram actually does it: the type family ACK x y represents
computations of Ackermann's function, and its constructor, ret, just
packs up a pair of arguments and the corresponding answer; the function
call x y (ret x y n) => n
just unpacks the number. The idea is that you can read (call x y blah)
as a call to (acktual x y), provided you can find some blah which shows
that the computation is possible. Of course, we implement acktual by
showing that the computation is always possible. The definition is just
like ackFun, but you can see your pattern matching program in there:
each ret is a line of the program; each call is a recursive call,
explained by appeal to an inductive hypothesis whose type says which ACK
computations it's willing to perform. Moreover, when you run acktual on
open terms, you can read off the result of the pattern matching program,
just by translating (call x y blah) as (acktual x y): as you can see,
the blah is often large and boring.
The moral of the story is this: if you know how to do induction, you get
a programming language for free!
Meanwhile, as far as the majority function is concerned...
[For the uninitiated, this is G\'erard Berry's notorious counterexample
to the correspondence between pattern matching and case expressions when
operating over open terms, it goes like this
maj :: Bool -> Bool -> Bool -> Bool
maj True True True = True
maj True False b = b
maj False b True = b
maj b True False = b
maj False False False = False
Its patterns are clearly exhaustive and disjoint, but there is no tree
of case splittings which delivers those five cases, hence no Epigram
program maj which satisfies all five of those laws computationally.]
Exercise: find an Epigram definition of majority, extensionally equal to
maj) with fewer cases. You'll discover that some of the computation laws
for your solution only hold propositionally for Berry's definition.
When you compute with values, maj is (deliberately on Berry's part) a
very silly way to compute the result: by the time you're running line 4,
you're bound to know what b is. Case splitting trees are the efficient
way to compile pattern matching (Augustsson, 1985), testing each
constructor tag at most once. It's an interesting question: can/should
we decouple the implementation of a program we want to run from the
decidable equational theory we want to exploit when reasoning about it.
I can't imagine wanting to run Berry's function, but I can imagine
situtations where those are exactly the laws I need.
Same sort of stuff happens when you define things like simultaneous
substitution by recursion on terms: the identity substitution
is only extensionally the identity function. We can never guarantee to
win this game by crafty choice of computation rules alone (although you
can often do better than you think). Reflection is the way to explain
the art of the possible here.
But the reason Epigram uses eliminators is not that we're especially
attached to the standard elimination constants for inductive datatypes;
it's precisely that we're *not* attached to them. We can program with
*any* induction theorem we can state, and we can even run those programs
if we can prove the induction theorems they rely on. If your notion of
pattern is just based on constructors, you ain't seen nothing. Why not
read 'The view from the left'?
All the best
Conor
------------------------------------------------------------------------------
( n : Nat !
data (---------! where (------------! ; !-------------!
! Nat : * ) ! zero : Nat ) ! suc n : Nat )
------------------------------------------------------------------------------
( ( p : P m ! !
! n : Nat ; P : Nat -> * ; mz : P zero ; !--------------------! !
! ! ms m p : P (suc m) ) !
let !------------------------------------------------------------------!
! elim n P mz ms : P n )
elim n P mz ms <= rec n
{ elim n P mz ms <= case n
{ elim zero P mz ms => mz
elim (suc n) P mz ms => ms n (elim n P mz ms)
}
}
------------------------------------------------------------------------------
( x, y : Nat !
let !---------------!
! ack x y : Nat )
ack x y <= elim x
{ ack zero y => suc y
ack (suc m) y <= elim y
{ ack (suc m) zero => ack m (suc zero)
ack (suc m) (suc m') => ack m (ack (suc m) m')
}
}
------------------------------------------------------------------------------
inspect lam n : Nat => ack zero n => (lam n => suc n) : all n : Nat => Nat
------------------------------------------------------------------------------
inspect lam m : Nat => ack (suc m) zero
=> (lam m => ack m (suc zero)) : all m : Nat => Nat
------------------------------------------------------------------------------
inspect lam m, n : Nat => ack (suc m) (suc n)
=>
(lam m ; n => ack m (ack (suc m) n))
: all m : Nat ; n : Nat => Nat
------------------------------------------------------------------------------
( x, y : Nat !
let !-----------------!
! AckType x y : * )
AckType x y => Nat
------------------------------------------------------------------------------
( x, y : Nat !
let !--------------------------!
! ackFun x y : AckType x y )
ackFun x y
=>
elim x (lam x : Nat => all y : Nat => AckType x y) (lam y => suc y)
% (lam m : Nat ; mh : all y : Nat => AckType m y ; y' : Nat!
!=> elim y' (lam y' => AckType (suc m) y') (mh (suc zero)) !
! % (lam m' ; m'h => mh m'h) )
% y
------------------------------------------------------------------------------
let (------------------------------!
! law1 : ackFun zero n = suc n )
law1 => refl
------------------------------------------------------------------------------
let (--------------------------------------------------!
! law2 : ackFun (suc m) zero = ackFun m (suc zero) )
law2 => refl
------------------------------------------------------------------------------
let (---------------------------------------------------------------!
! law3 : ackFun (suc m) (suc m') = ackFun m (ackFun (suc m) m') )
law3 => refl
------------------------------------------------------------------------------
( x, y : Nat ! ( x, y : Nat ; n : Nat !
data !-------------! where !-----------------------!
! ACK x y : * ) ! ret x y n : ACK x y )
------------------------------------------------------------------------------
( c : ACK x y !
let !------------------!
! call x y c : Nat )
call x y c <= case c
{ call x y (ret x y n) => n
}
------------------------------------------------------------------------------
( x, y : Nat !
let !-------------------!
! acktual x y : Nat )
acktual x y
=>
call x y
% (elim x (lam x => all y => ACK x y) (lam y => ret zero y (suc y))!
!% (lam m ; mh ; y' => ! !
! ! elim y' (lam y' => ACK (suc m) y') ! !
! ! % (ret (suc m) zero (call m (suc zero) (mh (suc zero)))) ! !
! ! % (lam m' ; m'h => !! !
! ! ! ret (suc m) (suc m') (call m (call (suc m) m' m'h)!!! !
! ! ! !% (mh (call (suc m) m' m'h))))) !
!% y )
------------------------------------------------------------------------------
inspect lam n => acktual zero n => (lam n => suc n) : all n : Nat => Nat
------------------------------------------------------------------------------
inspect lam m => acktual (suc m) zero
=>
(lam m => !
! call m (suc zero) !
! % (elim m (lam x => all y : Nat => ACK x y) !!
! !% (lam y => ret zero y (suc y)) !!
! !% (lam m' ; mh ; y' => !!!
! ! ! elim y' (lam y'' => ACK (suc m') y'') !!!
! ! ! % (ret (suc m') zero (call m' (suc zero)!!!!!
! ! ! ! !% (mh (suc zero)) ))!!!
! ! ! % (lam m'' ; m'h => ! !!!
! ! ! ! ret (suc m') (suc m'') ! !!!
! ! ! ! % (call m' (call (suc m') m'' m'h)!! !!!
! ! ! ! !% (mh (call (suc m') m'' m'h)) )) )!!
! !% (suc zero) ))
: all m : Nat => Nat
------------------------------------------------------------------------------
inspect lam m ; n => acktual (suc m) (suc n)
=>
(lam m ; n => !
! call m !
! % (call (suc m) n ! !
! !% (elim n (lam y' => ACK (suc m) y') !! !
! ! !% (ret (suc m) zero !!! !
! ! ! !% (call m (suc zero) !!!! !
! ! ! ! !% (elim m (lam x => all y : Nat => ACK x y) !!!!! !
! ! ! ! ! !% (lam y => ret zero y (suc y)) !!!!! !
! ! ! ! ! !% (lam m' ; mh ; y' => !!!!!! !
! ! ! ! ! ! ! elim y' (lam y'' => ACK (suc m') y'')!!!!!! !
! ! ! ! ! ! ! % (ret (suc m') zero ! !!!!!! !
! ! ! ! ! ! ! !% (call m' (suc zero)!! !!!!!! !
! ! ! ! ! ! ! ! !% (mh (suc zero)) )) !!!!!! !
! ! ! ! ! ! ! % (lam m'' ; m'h => ! !!!!!! !
! ! ! ! ! ! ! ! ret (suc m') (suc m'') ! !!!!!! !
! ! ! ! ! ! ! ! % (call m' !! !!!!!! !
! ! ! ! ! ! ! ! !% (call (suc m') m'' m'h) !! !!!!!! !
! ! ! ! ! ! ! ! !% (mh (call (suc m') m''!!!! !!!!!! !
! ! ! ! ! ! ! ! ! ! !% m'h )))) )!!!!! !
! ! ! ! ! !% (suc zero) )))!! !
! ! !% (lam m' ; m'h => ! !! !
! ! ! ! ret (suc m) (suc m') ! !! !
! ! ! ! % (call m (call (suc m) m' m'h) !! !! !
! ! ! ! !% (elim m (lam x => all y : Nat => ACK x y)!!! !! !
! ! ! ! ! !% (lam y => ret zero y (suc y)) !!! !! !
! ! ! ! ! !% (lam m'' ; mh ; y' => ! !!! !! !
! ! ! ! ! ! ! elim y' (lam y'' => ! ! !!! !! !
! ! ! ! ! ! ! ! ACK (suc m'') y'') ! !!! !! !
! ! ! ! ! ! ! % (ret (suc m'') zero ! ! !!! !! !
! ! ! ! ! ! ! !% (call m'' (suc zero)!! ! !!! !! !
! ! ! ! ! ! ! ! !% (mh (suc zero)) )) ! !!! !! !
! ! ! ! ! ! ! % (lam m''' ; m'h' => !! !!! !! !
! ! ! ! ! ! ! ! ret (suc m'') (suc m''') !! !!! !! !
! ! ! ! ! ! ! ! % (call m'' !!! !!! !! !
! ! ! ! ! ! ! ! !% (call (suc m'') m'''!!!! !!! !! !
! ! ! ! ! ! ! ! ! !% m'h' )!!! !!! !! !
! ! ! ! ! ! ! ! !% (mh (call (suc m'')!!!!! !!! !! !
! ! ! ! ! ! ! ! ! ! !% m''' !!!!! !!! !! !
! ! ! ! ! ! ! ! ! ! !% m'h' ))))) !!! !! !
! ! ! ! ! !% (call (suc m) m' m'h) ))) )) !
! % (elim m (lam x => all y : Nat => ACK x y) !!
! !% (lam y => ret zero y (suc y)) !!
! !% (lam m' ; mh ; y' => ! !!
! ! ! elim y' (lam y'' => ACK (suc m') y'') ! !!
! ! ! % (ret (suc m') zero (call m' (suc zero)!!! !!
! ! ! ! !% (mh (suc zero)) ))! !!
! ! ! % (lam m'' ; m'h => ! ! !!
! ! ! ! ret (suc m') (suc m'') ! ! !!
! ! ! ! % (call m' (call (suc m') m'' m'h)!! ! !!
! ! ! ! !% (mh (call (suc m') m'' m'h)) )) ) !!
! !% (call (suc m) n !!!
! ! !% (elim n (lam y' => ACK (suc m) y') !!!!
! ! ! !% (ret (suc m) zero !!!!!
! ! ! ! !% (call m (suc zero) !!!!!!
! ! ! ! ! !% (elim m (lam x => all y : Nat => ACK x y)!!!!!!!
! ! ! ! ! ! !% (lam y => ret zero y (suc y)) !!!!!!!
! ! ! ! ! ! !% (lam m' ; mh ; y' => ! !!!!!!!
! ! ! ! ! ! ! ! elim y' (lam y'' => ! ! !!!!!!!
! ! ! ! ! ! ! ! ! ACK (suc m') y'') ! !!!!!!!
! ! ! ! ! ! ! ! % (ret (suc m') zero ! ! !!!!!!!
! ! ! ! ! ! ! ! !% (call m' (suc zero)!! ! !!!!!!!
! ! ! ! ! ! ! ! ! !% (mh (suc zero)) )) ! !!!!!!!
! ! ! ! ! ! ! ! % (lam m'' ; m'h => !! !!!!!!!
! ! ! ! ! ! ! ! ! ret (suc m') (suc m'') !! !!!!!!!
! ! ! ! ! ! ! ! ! % (call m' !!! !!!!!!!
! ! ! ! ! ! ! ! ! !% (call (suc m') m''! !!! !!!!!!!
! ! ! ! ! ! ! ! ! ! !% m'h ) !!! !!!!!!!
! ! ! ! ! ! ! ! ! !% (mh (call (suc m')!!!!! !!!!!!!
! ! ! ! ! ! ! ! ! ! ! !% m'' !!!!! !!!!!!!
! ! ! ! ! ! ! ! ! ! ! !% m'h ))))) !!!!!!!
! ! ! ! ! ! !% (suc zero) )))!!!!
! ! ! !% (lam m' ; m'h => ! !!!!
! ! ! ! ! ret (suc m) (suc m') ! !!!!
! ! ! ! ! % (call m (call (suc m) m' m'h) !! !!!!
! ! ! ! ! !% (elim m (lam x => ! !!! !!!!
! ! ! ! ! ! ! ! all y : Nat => ACK x y) !!! !!!!
! ! ! ! ! ! !% (lam y => ret zero y (suc y)) !!! !!!!
! ! ! ! ! ! !% (lam m'' ; mh ; y' => !!!! !!!!
! ! ! ! ! ! ! ! elim y' (lam y'' => ! !!!! !!!!
! ! ! ! ! ! ! ! ! ACK (suc m'') y'') !!!! !!!!
! ! ! ! ! ! ! ! % (ret (suc m'') zero ! !!!! !!!!
! ! ! ! ! ! ! ! !% (call m'' (suc zero)!! !!!! !!!!
! ! ! ! ! ! ! ! ! !% (mh (suc zero)) )) !!!! !!!!
! ! ! ! ! ! ! ! % (lam m''' ; m'h' => !!!!! !!!!
! ! ! ! ! ! ! ! ! ret (suc m'') (suc m''')!!!!! !!!!
! ! ! ! ! ! ! ! ! % (call m'' !!!!!! !!!!
! ! ! ! ! ! ! ! ! !% (call (suc m'')! !!!!!! !!!!
! ! ! ! ! ! ! ! ! ! !% m''' ! !!!!!! !!!!
! ! ! ! ! ! ! ! ! ! !% m'h' ) !!!!!! !!!!
! ! ! ! ! ! ! ! ! !% (mh (call !!!!!!!! !!!!
! ! ! ! ! ! ! ! ! ! ! !% (suc m'')!!!!!!!! !!!!
! ! ! ! ! ! ! ! ! ! ! !% m''' !!!!!!!! !!!!
! ! ! ! ! ! ! ! ! ! ! !% m'h' )))))!!! !!!!
! ! ! ! ! ! !% (call (suc m) m' m'h) ))) ))))
: all m : Nat ; n : Nat => Nat
------------------------------------------------------------------------------