# typecheck-plugin-nat-simple

## what's this

This package provide plugin which extend type checking of Nat.
The type checker can calculate only addition, subtraction and less or equal of
constants and variables.

(View sample code on directory sample/.)

## motivation

Suppose you need lengthed list. You define like following.
(View `sample/lengthed_tail.hs`

)

```
import GHC.TypeNats
infixr 6 :.
data List :: Nat -> * -> * where
Nil :: List 0 a
(:.) :: a -> List ln a -> List (ln + 1) a
```

And you want to define function `tail`

.

```
tail_ :: List (n + 1) a -> List n a
tail_ Nil = error "tail_: Nil"
tail_ (_ :. xs) = xs
```

But it cause type check error.

```
error:
・Could not deduce: ln ~ n
from the context: (n + 1) ~ (ln + 1)
...
```

Type checker say "I cannot derive (ln == n) from (n + 1 == ln + 1)".
But it seems to be obvious.
You can use this plugin like following.

```
{-# OPTIONS_GHC -fplugin=Plugin.TypeCheck.Nat.Simple #-}
```

Add it to top of the code, then type check succeed.

## more example

To show more example, I will use Data.Proxy.Proxy.
First examle is following (View `sample/mplus1_nplus1.hs`

).

```
foo :: (m + 1) ~ (n + 1) => Proxy m -> Proxy n
foo = id
```

If you don't use this plugin, then following error occur.

```
・Could not deduce: m ~ n
from the context: (m + 1) ~ (n + 1)
...
```

Use this plugin, you can compile it.

Second example is following (View `sample/two_equations.hs`

).

```
foo :: ((x + y) ~ z, (w - y) ~ v) => Proxy (x + w) -> Proxy (z + v)
foo = id
```

Without this plugin, following error occur.

```
・Could not deduce: (x + w) ~ (z + v)
from the context: ((x + y) ~ z, (w - y) ~ v)
...
```

Use this plugin, you can compile it.

## error and recovery

### type check error

See following code.

```
foo :: Proxy (n - 1 + 1) -> Proxy n
foo = id
```

This cause type check error even if you use this plugin.

```
・Couldn't match type `n' with `(n - 1) + 1'
...
```

### research

What's wrong?
You can see type check log of this plugin like following.

```
% stack ghc sample/minus1plus1.hs -- -ddump-tc-trace 2>&1 | grep -A 20 'Plugin.TypeCheck.Nat.Simple'
...
givens ([Exp v 'Boolean]): given (Givens v): (Givens [])
exBool: not boolean: (n_a1s2[sk:1] - 1) + 1
wanted (Exp v 'Boolean): ((((Var n_a1s2[sk:1]) :- (Const 1)) :+ (Const 1)) :== (Var n_a1s2[sk:1]))
wanted (Wanted v): (Wanted [(0 == 0), (1 * n_a1s2[sk:1] >= 0), (-1 + 1 * n_a1s2[sk:1] >= 0), (1 * n_a1s2[sk:1] >= 0)])
canDerive1: (0 == 0) is self-contained
canDerive1: (1 * n_a1s2[sk:1] >= 0) is self-contained
canDerive1: (-1 + 1 * n_a1s2[sk:1] >= 0) cannot be derived from
canDerive1: (1 * n_a1s2[sk:1] >= 0) is self-contained
result: type checker: return False
...
```

See the line of `canDerive1: (- 1 + 1 * n_a1s2[sk:1] >= 0) cannot be derived from`

.
It mean "`-1 + n_a1s2[sk:1]`

should be greater or equal than 0. But no such context".

### try calculation more portably

You can try to caluculate more simply.

```
% stack ghci
> :set -XTypeApplications
> :module Data.Derivation.Parse Data.Derivation.CanDerive Control.Monad.Try Data.Maybe
> parseConstraint "n - 1 + 1 == n"
Just ((:==) ((:+) ((:=) (Var "n") (Const 1)) (Const 1)) (Var "n"))
> Just w = wanted @String <$> it
> gs = givens @String []
> fst . runTry $ uncurry canDerive =<< (,) <$> gs <*> w
Right False
```

The wanted constraint cannot be derived from empty given constraint.
Let's add `1 <= n`

constraint.

```
> gs = given @String . maybeToList $ parseConstraint "1 <= n"
> fst . runTry $ uncurry canDerive =<< (,) <$> gs <*> w
Right True
```

OK! It succeed if you add `1 <= n`

to given constraint.

### recovery

Let's add `1 <= n`

context like following.

```
foo :: 1 <= n => Proxy (n - 1 + 1) -> Proxy n
foo = id
```

Then it succeed to type check.