Sergei Meshveliani
2018-08-21 11:09:00 UTC
(what is a correct place to submit proposals on the language?)
Dear Agda developers,
I propose to add to Agda _language_ the construct of well-founded
recursion
(see the attachment to this letter).
I believe it is easy to implement.
The extension will preserve all the existing applied code.
The reason for adding this construct is that using the feature of
WellFounded of Standard library complicates programs essentially.
Example
-------
----------------------------------------------
data Bin : Set -- A new definition of Bin
where
0# : Bin
2suc : Bin â Bin -- \nâ 2*(1+n) arbitrary nonzero even
suc2* : Bin â Bin -- \nâ 1 + 2n arbitrary odd
{-# TERMINATING #-}
logâ : (x : Bin) â x ⢠0# â â -- integer part of (logarithmâ x)
logâ 0# 0â¢0 = contradiction refl 0â¢0
logâ (2suc x) _ = 1+ (logâ (suc x) sucâ¢0) -- (*)
logâ (suc2* 0#) _ = 0
logâ (suc2* (2suc x)) _ = 1+ (logâ (2suc x) 2sucâ¢0)
logâ (suc2* (suc2* x)) _ = 1+ (logâ (suc2* x) suc2*â¢0)
---------------------------------------------------------
`TERMINATING' is set because the rule of (*) is not by a structural
recursion.
Then I prove various properties:
---------------------------------------------------------
logââ2* : (x : Bin) â (nz : x ⢠0#) â (2xâ¢0 : 2* x ⢠0#) â
logâ (2* x) 2xâ¢0 â¡ 1+ (logâ x nz)
logââ2* 0# nz _ = contradiction refl nz
logââ2* (2suc _) _ _ = refl
logââ2* (suc2* x) x'â¢0 2x'â¢0 = ...
...
---------------------------------------------------------
To remove `TERMINATING', it is needed either to add some counter
processing to the loop or to apply WellFounded recursion on Bin that
uses the inequality suc x < 2suc x.
The first approach complicates the program greatly. It becomes
considerably more complex than the fragment in possible textbook or a
paper:
< The source of logâ >
++
"(*) terminates, because IsWellFoundedOrder _<_ and because
lt : suc x < 2suc x
lt = BinOrd.suc-x<2suc-x x
".
The second approach currently is possible only by using the feature of
WellFounded of Standard library:
----------------------------------------------------------------
open All <-wellFounded using () renaming (wfRec to <-rec)
P : Bin â Set
P x = (x ⢠0#) â â
logâ : (a : Bin) â P a -- signature changed
logâ = <-rec _ P log
where
log : (a : Bin) â (â x â x < a â P x) â P a
log 0# _ 0â¢0 = contradiction refl 0â¢0
log (2suc x) logRec _ =
1+ (logRec (suc x) suc-x<x' sucâ¢0)
where
suc-x<x' =
begin suc x <[ x<2x (suc x) sucâ¢0 ]
2* (suc x) â¡[ sym (2suc-asâ x) ]
2suc x
â
log (suc2* 0#) _ _ = 0
log (suc2* (2suc x)) logRec _ =
1+ (logRec x' (x<suc2*-x x') 2sucâ¢0)
where
x' = 2suc x
log (suc2* (suc2* x)) logRec _ =
1+ (logRec x' (x<suc2*-x x') suc2*â¢0)
where
x' = suc2* x
----------------------------------------------------------------
A proof for suc-x<x' is all right, it is needed in any approach.
But the program is complicated greatly:
the signature of
(a : Bin) â a ⢠0# â â
is changed to (a : Bin) â P a,
the call (<-rec _ P log) appears, with a certain complicated signature
for log.
Furthermore, the proofs are broken. For example, the rule
logââ2* (2suc _) _ _ = refl
is not type-checked any more.
I expect difficulties in reorganizing proofs.
Proposal
========
To add to Agda _language_ the construct of well-founded recursion --
in the following way.
1. Add to Standard library the notion of IsWellFoundedOrder :
--------------------------------------------------------------------
Sequence : â {α} â (A : Set α) â Set α -- infinite sequences in A
Sequence A = â â A
module OfWellFounded {α α= α<} (PO : StrictPartialOrder α α= α<)
where
open StrictPartialOrder PO using (_â_; _<_) renaming (Carrier to C)
_â€_ : Rel C _
x †y = x < y â x â y
DecrementBreak : Sequence C â Set _
DecrementBreak f = â (\n â f n †f (suc n))
IsWellFoundedOrder : Set _
IsWellFoundedOrder = (f : Sequence C) â DecrementBreak f
--------------------------------------------------------------------
The property is "any sequence has a decrement break".
2. The language construct is as in the following example with logâ:
---------------------------------------------------------------
logâ : (x : Bin) â x ⢠0# â â -- signatures do not change!
logâ 0# 0â¢0 = contradiction refl 0â¢0
logâ (2suc x) _ =
1+ (recurseWith isWellFoundedOrder lt
(logâ (suc x) sucâ¢0)
)
where
lt : suc x < 2suc x
lt = Ord0.suc-x<2suc-x x
logâ (suc2* 0#) _ = 0
logâ (suc2* (2suc x)) _ = 1+ (logâ (2suc x) 2sucâ¢0)
logâ (suc2* (suc2* x)) _ = 1+ (logâ (suc2* x) suc2*â¢0)
---------------------------------------------------------------
Here the result of
(recurseWith isWellFoundedOrder lt (logâ (suc x) sucâ¢0))
is
(logâ (suc x) sucâ¢0).
recurseWith is a built-in function.
In this example, it makes the type checker to do the following.
It checks the proofs for lt and isWellFoundedOrder,
and thus it concludes that this recursive call of logâ is well founded.
As other rules for logâ use structural recursion, it concludes that
logâ is terminating.
This is a termination _axiom_ by IsWellFoundedOrder applied by the
_type checker_.
How difficult is it to implement in the type checker?
The user program remains clear.
I hope, the proofs written for the `TERMINATING' version will remain.
It is easy to prove IsWellFoundedOrder for Nat._<_, Bin._<_, and so
on.
I appreciate: WellFounded of Standard is a clever way to bring a
traditional termination proof tool without extending language.
Programmers are free to use it.
But it is also desirable to give the possibility to write "normal"
programs, in the style of somewhat an ordinary mathematical paper, that
can be understood by mathematicians (and by the program author himself).
Advanced algorithms in mathematics need effort to program.
Many people will like rather to use this small language extension than
to complicate further the program by artificially changing signatures in
a clever way.
I append to this letter the full source for the IsWellFoundedOrder
feature, together with its implementation for â.
Regards,
------
Sergei
Dear Agda developers,
I propose to add to Agda _language_ the construct of well-founded
recursion
(see the attachment to this letter).
I believe it is easy to implement.
The extension will preserve all the existing applied code.
The reason for adding this construct is that using the feature of
WellFounded of Standard library complicates programs essentially.
Example
-------
----------------------------------------------
data Bin : Set -- A new definition of Bin
where
0# : Bin
2suc : Bin â Bin -- \nâ 2*(1+n) arbitrary nonzero even
suc2* : Bin â Bin -- \nâ 1 + 2n arbitrary odd
{-# TERMINATING #-}
logâ : (x : Bin) â x ⢠0# â â -- integer part of (logarithmâ x)
logâ 0# 0â¢0 = contradiction refl 0â¢0
logâ (2suc x) _ = 1+ (logâ (suc x) sucâ¢0) -- (*)
logâ (suc2* 0#) _ = 0
logâ (suc2* (2suc x)) _ = 1+ (logâ (2suc x) 2sucâ¢0)
logâ (suc2* (suc2* x)) _ = 1+ (logâ (suc2* x) suc2*â¢0)
---------------------------------------------------------
`TERMINATING' is set because the rule of (*) is not by a structural
recursion.
Then I prove various properties:
---------------------------------------------------------
logââ2* : (x : Bin) â (nz : x ⢠0#) â (2xâ¢0 : 2* x ⢠0#) â
logâ (2* x) 2xâ¢0 â¡ 1+ (logâ x nz)
logââ2* 0# nz _ = contradiction refl nz
logââ2* (2suc _) _ _ = refl
logââ2* (suc2* x) x'â¢0 2x'â¢0 = ...
...
---------------------------------------------------------
To remove `TERMINATING', it is needed either to add some counter
processing to the loop or to apply WellFounded recursion on Bin that
uses the inequality suc x < 2suc x.
The first approach complicates the program greatly. It becomes
considerably more complex than the fragment in possible textbook or a
paper:
< The source of logâ >
++
"(*) terminates, because IsWellFoundedOrder _<_ and because
lt : suc x < 2suc x
lt = BinOrd.suc-x<2suc-x x
".
The second approach currently is possible only by using the feature of
WellFounded of Standard library:
----------------------------------------------------------------
open All <-wellFounded using () renaming (wfRec to <-rec)
P : Bin â Set
P x = (x ⢠0#) â â
logâ : (a : Bin) â P a -- signature changed
logâ = <-rec _ P log
where
log : (a : Bin) â (â x â x < a â P x) â P a
log 0# _ 0â¢0 = contradiction refl 0â¢0
log (2suc x) logRec _ =
1+ (logRec (suc x) suc-x<x' sucâ¢0)
where
suc-x<x' =
begin suc x <[ x<2x (suc x) sucâ¢0 ]
2* (suc x) â¡[ sym (2suc-asâ x) ]
2suc x
â
log (suc2* 0#) _ _ = 0
log (suc2* (2suc x)) logRec _ =
1+ (logRec x' (x<suc2*-x x') 2sucâ¢0)
where
x' = 2suc x
log (suc2* (suc2* x)) logRec _ =
1+ (logRec x' (x<suc2*-x x') suc2*â¢0)
where
x' = suc2* x
----------------------------------------------------------------
A proof for suc-x<x' is all right, it is needed in any approach.
But the program is complicated greatly:
the signature of
(a : Bin) â a ⢠0# â â
is changed to (a : Bin) â P a,
the call (<-rec _ P log) appears, with a certain complicated signature
for log.
Furthermore, the proofs are broken. For example, the rule
logââ2* (2suc _) _ _ = refl
is not type-checked any more.
I expect difficulties in reorganizing proofs.
Proposal
========
To add to Agda _language_ the construct of well-founded recursion --
in the following way.
1. Add to Standard library the notion of IsWellFoundedOrder :
--------------------------------------------------------------------
Sequence : â {α} â (A : Set α) â Set α -- infinite sequences in A
Sequence A = â â A
module OfWellFounded {α α= α<} (PO : StrictPartialOrder α α= α<)
where
open StrictPartialOrder PO using (_â_; _<_) renaming (Carrier to C)
_â€_ : Rel C _
x †y = x < y â x â y
DecrementBreak : Sequence C â Set _
DecrementBreak f = â (\n â f n †f (suc n))
IsWellFoundedOrder : Set _
IsWellFoundedOrder = (f : Sequence C) â DecrementBreak f
--------------------------------------------------------------------
The property is "any sequence has a decrement break".
2. The language construct is as in the following example with logâ:
---------------------------------------------------------------
logâ : (x : Bin) â x ⢠0# â â -- signatures do not change!
logâ 0# 0â¢0 = contradiction refl 0â¢0
logâ (2suc x) _ =
1+ (recurseWith isWellFoundedOrder lt
(logâ (suc x) sucâ¢0)
)
where
lt : suc x < 2suc x
lt = Ord0.suc-x<2suc-x x
logâ (suc2* 0#) _ = 0
logâ (suc2* (2suc x)) _ = 1+ (logâ (2suc x) 2sucâ¢0)
logâ (suc2* (suc2* x)) _ = 1+ (logâ (suc2* x) suc2*â¢0)
---------------------------------------------------------------
Here the result of
(recurseWith isWellFoundedOrder lt (logâ (suc x) sucâ¢0))
is
(logâ (suc x) sucâ¢0).
recurseWith is a built-in function.
In this example, it makes the type checker to do the following.
It checks the proofs for lt and isWellFoundedOrder,
and thus it concludes that this recursive call of logâ is well founded.
As other rules for logâ use structural recursion, it concludes that
logâ is terminating.
This is a termination _axiom_ by IsWellFoundedOrder applied by the
_type checker_.
How difficult is it to implement in the type checker?
The user program remains clear.
I hope, the proofs written for the `TERMINATING' version will remain.
It is easy to prove IsWellFoundedOrder for Nat._<_, Bin._<_, and so
on.
I appreciate: WellFounded of Standard is a clever way to bring a
traditional termination proof tool without extending language.
Programmers are free to use it.
But it is also desirable to give the possibility to write "normal"
programs, in the style of somewhat an ordinary mathematical paper, that
can be understood by mathematicians (and by the program author himself).
Advanced algorithms in mathematics need effort to program.
Many people will like rather to use this small language extension than
to complicate further the program by artificially changing signatures in
a clever way.
I append to this letter the full source for the IsWellFoundedOrder
feature, together with its implementation for â.
Regards,
------
Sergei