Reputation: 81
I want to make a subtraction of Natural Number work. However, the argument of the function have a premise that forall a, b in N ; a >= b. so I make some related functions:
data ℕ : Set where
zero : ℕ
suc : ℕ → ℕ
data _NotLessThan_ : (n m : ℕ) → Set where
ZERO : zero NotLessThan zero
SUC_ZERO : ∀ n → (suc n) NotLessThan zero
STEP : ∀ m n → m NotLessThan n → (suc m) NotLessThan (suc n)
sub : (m n : ℕ) → (a : m NotLessThan n) → ℕ
sub zero (suc n) () -- zero - n can't return
sub zero zero _ = zero
sub (suc m) zero _ = (suc m)
sub (suc x) (suc y) a = sub x y (x NotLessThan y)
However, I get the error:
Set !=< x NotLessThan y of type Set₁
when checking that the expression x NotLessThan y has type
x NotLessThan y
I find tht the type is x NotLessThan y
as I excepted. Is there any type error? How to debug this kind of type error or how to declare a function to skip the type-detecting error?
Upvotes: 3
Views: 229
Reputation: 1347
There is a type incompatibility in your code snippet, namely that (x NotLessThan y)
is a type, and not a value of type (x NotLessThan y)
, which you have to provide in order for your function to be type correct. The following snippets corrects the issue:
sub : (m n : ℕ) → (a : m NotLessThan n) → ℕ
sub .zero .zero ZERO = zero
sub .(suc n) .zero SUC n ZERO = suc n
sub .(suc m) .(suc n) (STEP m n a) = sub m n a
This is the main problem in your code, but there are also several inaccuracies that prevented you from writing a correct code. These are as follow:
Your data type name _NotLessThan_
can somewhat be improved to better match mathematical concepts (and to match what you will find in the standard library).
Constructors are not usually written with capital letters.
You provide two constructors ZERO
and SUC_ZERO
that have the same purpose, that is saying that any natural number is greater or equal than zero. These can be factorized.
Your second constructor SUC_ZERO
contains an underscore, which Agda will interpret as the location where the parameter for this constructor will be placed. I feel like this was not intended, in which case you should definitely avoid underscores in constructor names.
In your function sub
you case-split on your two natural variables although the third argument (the proof that a is greater that b) already contains the necessary information for the computation to happens. Pattern matching on this argument instead will also avoid any empty cases.
Taking these remarks into account leads to the following definition for your data type:
data _≤_ : ℕ → ℕ → Set where
z≤s : ∀ {a} → zero ≤ a
s≤s : ∀ {a b} → a ≤ b → suc a ≤ suc b
From this data type, the sub function is then written very naturally:
sub' : ∀ a b → b ≤ a → ℕ
sub' a .zero z≤s = a
sub' ._ ._ (s≤s p) = sub' _ _ p
Note that in this snippet underscores (and dots on the left side of the definition) are used to mark parameters whose value can be inferred by Agda, which makes visible the fact that the proof is sufficient to compute the subtraction.
This can be interesting to let Agda compute the proof alone instead of having to provide it whenever you want to make a subtraction. In order to do so, your data type has to be proven decidable, which means that, given two natural numbers a
and b
, a function has to be written that either computes the proof that a ≤ b
or the negation of it. This requires several steps, the first being the definition of the logical false (the empty type):
data ⊥ : Set where
From this data type, the logical negation can be defined:
¬ : Set → Set
¬ A = A → ⊥
We can now define the type of decidable types (roughly, either it can be proven true or false):
data Dec (A : Set) : Set where
yes : A → Dec A
no : ¬ A → Dec A
Then, we can prove that for any input, the lesser or equal data type is decidable regarding this definition:
_≤?_ : ∀ a b → Dec (a ≤ b)
zero ≤? b = yes z≤s
suc a ≤? zero = no (λ ())
suc a ≤? suc b with a ≤? b
suc a ≤? suc b | yes p = yes (s≤s p)
suc a ≤? suc b | no ¬p = no λ {(s≤s q) → ¬p q}
In order to encapsulate the error case for the subtraction, we need the maybe monad, which is either and error case, or a value:
data Maybe (A : Set) : Set where
nothing : Maybe A
just : A → Maybe A
Finally, we can write a subtraction that takes two natural numbers, checks whether the satisfy the requirement for the subtraction, returns an error if not, and computes it when possible:
_-M_ : (a b : ℕ) → Maybe ℕ
a -M b with b ≤? a
a -M b | yes p = just (sub' a b p)
a -M b | no _ = nothing
As an example of failure, here is an unsound subtraction:
error : Maybe ℕ
error = suc (suc (zero)) -M (suc (suc (suc (suc (suc (suc zero))))))
Here is what Agda gives when evaluating error
:
nothing
As an example of success, here is a sound subtraction:
success : Maybe ℕ
success = (suc (suc (suc (suc (suc (suc zero)))))) -M suc (suc (zero))
Here is what Agda gives when evaluating success
:
just (suc (suc (suc (suc zero))))
Note that all of these quantities (up until the definition of the maybe data type) can be found in the standard library, and that I deliberately omitted universe levels when presenting them.
Upvotes: 1
Reputation: 646
The expression (x NotLessThan y)
is not of type (x NotLessThan y)
. NotLessThan
is a data definition of type set (an indexed one). You construct elements of NotLessThan
with its three constructors you have defined. In this case you have to pattern patch on a
, to get the appropriate constructor and the element of the type you need. So the last case would be
sub (suc x) (suc y) (STEP _ _ a) = sub x y a
Upvotes: 4