LINUX.ORG.RU

История изменений

Исправление monk, (текущая версия) :

(например, чтобы срабатывала проверка на входе одной функции рекурсивной фуркции и сочеталась с проверкой на выходе другой), то придётся понимать как устроены доказательства.

{-@ append :: xs:[a] -> ys:[a] -> {v:[a] | (len v) = (len xs) + (len ys)} @-}
append [] ys     = ys
append (x:xs) ys = x : append xs ys

?

Или покажи пример на зависимых типах.

в сложности доказательств (как и жидких)

В случае жидких всё, что нерекурсивно или рекурсивно по одному аргументу доказывается автоматически (как пример с append). То, что не доказывается автоматически, доказывается очень наглядно:

Например, есть рекурсивное определение фибоначчи

{-@ fib::{x:Int | x >=0} -> {r : Int|r>0} @-}
fib :: Int -> Int
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

условие, что результат больше нуля, компилятор выводит автоматически.

и с аккумулятором

{-@ fibacc::Int -> Int -> n:Int -> {n1:Int | n1 >=0 && (n1 >= n || n1 < 2)} -> Int / [n1-n] @-} 
fibacc :: Int -> Int -> Int -> Int -> Int
fibacc _ _ _ 0 = 1
fibacc _ _ _ 1 = 1
fibacc f1 f2 n n1 
 | n == n1 = f1 + f2
 | otherwise = fibacc f2 (f1+f2) (n+1) n1

{-@ fib2::{n:Int | n >=0} -> r:Int @-}
fib2 :: Int -> Int
fib2 n = fibacc 1 1 2 n

В фигурных скобках ограничения жидких типов.

Теперь, если надо доказать, что fib n = fib2 n для всех n можно попробовать просто дописать постусловие:

{-@ fib2::{n:Int | n >=0} -> {r:Int | r = fib n} @-}

Не получается. Компилятор недостаточно умён. Можно написать ему доказательство:

{- fibaccLemma :: f1:Int -> f2:Int -> {k:Int | k >= 0} -> {n:Int | n >=2 && n-k>=2 }  -> { fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc f1 f2 k (n-2) } / [n-k] @-}
fibaccLemma :: Int -> Int -> Int -> Int -> Proof
fibaccLemma f1 f2 k n | n - k == 2
    = fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc f1 f2 k (n-2)
    === fibacc f2 (f1+f2) (k+1) n == fibacc f2 (f1+f2) (k+1) (n-1) + f1 + f2
    === fibacc (f1+f2) (f1+f2+f2) (k+2) n == f2+f1+f2+f1+f2
    === f1+f2+f1+f2+f2 == f2+f1+f2+f1+f2
    *** QED
fibaccLemma f1 f2 k n | n - k > 2
    = fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc f1 f2 k (n-2)
    === fibacc f2 (f1+f2) (k+1) n == fibacc f2 (f1+f2) (k+1) (n-1) + fibacc f2 (f1+f2) (k+1) (n-2)
    ? fibaccLemma f2 (f1+f2) (k+1) n
    *** QED

{-@ fibTheorem :: {n:Int | n >= 0} -> { fib n == fib2 n } @-}
fibTheorem :: Int -> Proof 
fibTheorem 0 = fib 0 == fib2 0
    === 1 == fibacc 1 1 2 0
    === 1 == 1
    *** QED
fibTheorem 1 = fib 1 == fib2 1 
    === 1 == fibacc 1 1 2 1
    === 1 == 1 
    *** QED
fibTheorem n = fib n == fib2 n
    === fib (n-1) + fib (n-2) == fibacc 1 1 2 n
    ? fibaccLemma 1 1 2 n
    === fib (n-1) + fib (n-2) == fibacc 1 1 2 (n-1) + fibacc 1 1 2 (n-2)
    === fib (n-1) + fib (n-2) == fib2 (n-1) + fib2 (n-2)
    *** QED

В случае же зависимых типов доказательство получается почти нечитаемое.

Исходная версия monk, :

(например, чтобы срабатывала проверка на входе одной функции рекурсивной фуркции и сочеталась с проверкой на выходе другой), то придётся понимать как устроены доказательства.

{-@ append :: xs:[a] -> ys:[a] -> {v:[a] | (len v) = (len xs) + (len ys)} @-}
append [] ys     = ys
append (x:xs) ys = x : append xs ys

?

Или покажи пример на зависимых типах.

в сложности доказательств (как и жидких)

В случае жидких всё, что нерекурсивно или рекурсивно по одному аргументу доказывается автоматически (как пример с append). То, что не доказывается автоматически, доказывается очень наглядно:

Например, есть рекурсивное определение фибоначчи

{-@ fib::{x:Int | x >=0} -> {r : Int|r>0} @-}
fib :: Int -> Int
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

условие, что результат больше нуля, компилятор выводит автоматически.

и с аккумулятором

{-@ fibacc::Int -> Int -> n:Int -> {n1:Int | n1 >=0 && (n1 >= n || n1 < 2)} -> Int / [n1-n] @-} 
fibacc :: Int -> Int -> Int -> Int -> Int
fibacc _ _ _ 0 = 1
fibacc _ _ _ 1 = 1
fibacc f1 f2 n n1 
 | n == n1 = f1 + f2
 | otherwise = fibacc f2 (f1+f2) (n+1) n1

{-@ fib2::{n:Int | n >=0} -> r:Int @-}
fib2 :: Int -> Int
fib2 n = fibacc 1 1 2 n

В фигурных скобках ограничения жидких типов.

Теперь, если надо доказать, что fib n = fib2 n для всех n можно попробовать просто дописать постусловие:

{-@ fib2::{n:Int | n >=0} -> {r:Int | r = fib n} @-}

Не получается. Компилятор недостаточно умён. Можно написать ему доказательство:

{- fibaccLemma :: f1:Int -> f2:Int -> {k:Int | k >= 0} -> {n:Int | n >=2 && n-k>=2 }  -> { fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc_d f1 f2 k (n-2) } / [n-k] @-}
fibaccLemma :: Int -> Int -> Int -> Int -> Proof
fibaccLemma f1 f2 k n | n - k == 2
    = fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc f1 f2 k (n-2)
    === fibacc f2 (f1+f2) (k+1) n == fibacc f2 (f1+f2) (k+1) (n-1) + f1 + f2
    === fibacc (f1+f2) (f1+f2+f2) (k+2) n == f2+f1+f2+f1+f2
    === f1+f2+f1+f2+f2 == f2+f1+f2+f1+f2
    *** QED
fibaccLemma f1 f2 k n | n - k > 2
    = fibacc f1 f2 k n == fibacc f1 f2 k (n-1) + fibacc f1 f2 k (n-2)
    === fibacc f2 (f1+f2) (k+1) n == fibacc f2 (f1+f2) (k+1) (n-1) + fibacc f2 (f1+f2) (k+1) (n-2)
    ? fibaccLemma f2 (f1+f2) (k+1) n
    *** QED

{-@ fibTheorem :: {n:Int | n >= 0} -> { fib n == fib2 n } @-}
fibTheorem :: Int -> Proof 
fibTheorem 0 = fib 0 == fib2 0
    === 1 == fibacc 1 1 2 0
    === 1 == 1
    *** QED
fibTheorem 1 = fib 1 == fib2 1 
    === 1 == fibacc 1 1 2 1
    === 1 == 1 
    *** QED
fibTheorem n = fib n == fib2 n
    === fib (n-1) + fib (n-2) == fibacc 1 1 2 n
    ? fibaccLemma 1 1 2 n
    === fib (n-1) + fib (n-2) == fibacc 1 1 2 (n-1) + fibacc 1 1 2 (n-2)
    === fib (n-1) + fib (n-2) == fib2 (n-1) + fib2 (n-2)
    *** QED

В случае же зависимых типов доказательство получается почти нечитаемое.