ucsd-progsys / liquidhaskell

Liquid Types For Haskell

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reasoning steps with inequality

josedusol opened this issue · comments

Hello. Module Equational provides the ==. operator to do equational reasoning. What about inequaility?.

For example, suppose we want to prove something like:

{-@ filterLength :: p:(a -> Bool) -> l:List a -> { length (filter p l) <= length l } @-}

Then the reasoning chain would involve ==. steps but also "<=" steps. Is this possible?

Hi! Odd -- there certainly used to be -- see these:

(<=:) :: a -> a -> Proof -> a
{-@ (<=:) :: x:a -> y:a -> {v:Proof | x <= y } -> {v:a | v == x } @-}
(<=:) x y _ = x
(<:) :: a -> a -> Proof -> a
{-@ (<:) :: x:a -> y:a -> {v:Proof | x < y } -> {v:a | v == x } @-}
(<:) x y _ = x
(>:) :: a -> a -> Proof -> a
{-@ (>:) :: x:a -> y:a -> {v:Proof | x >y } -> {v:a | v == x } @-}
(>:) x _ _ = x
(==:) :: a -> a -> Proof -> a
{-@ (==:) :: x:a -> y:a -> {v:Proof| x == y} -> {v:a | v == x && v == y } @-}
(==:) x _ _ = x

I think you can just write your own following that template.

So

{-@ (<=.) :: x:a -> y:{a | x  <=  y} -> {v:a | v == y} @-}
(<=.) :: a -> a -> a 
_ ==. y = y 
{-# INLINE (<=.) #-} 

Thanks @ranjitjhala . But unfortunately, i can't use the proof combinator with an intermediate justification:

module Test where
import Language.Haskell.Liquid.Equational
import Prelude hiding (length,filter)

{-@ (<=.) :: x:a -> y:{a | x <= y} -> {v:a | v == x} @-}
(<=.) :: a -> a -> a 
x <=. y = x 
{-# INLINE (<=.) #-} 

{-@ reflect length @-}
{-@ length :: [a] -> Int @-}
length :: [a] -> Int
length = \l -> case l of { [] -> 0; x:xs -> length xs + 1 }  

{-@ reflect filter @-}
{-@ filter :: (a -> Bool) -> [a] -> [a] @-}
filter :: (a -> Bool) -> [a] -> [a]
filter = \p l -> case l of { [] -> []; x:xs -> case p x of { False -> filter p xs; True -> x:filter p xs }}

{-@ assume leqZero :: n:Nat -> { 0 <= n } @-}
leqZero :: Int -> Proof
leqZero n = ()

{-@ filterLength ::  p:(a -> Bool) -> l:[a] -> { length (filter p l) <= length l } @-}
filterLength :: (a -> Bool) -> [a] -> Proof  
filterLength p [] =
      length (filter p [])             
      ? filter p []
  ==. length []                        
      ? length []  
  ==. 0                               
      ? leqZero (length [])    -- TYPE ERROR if present
  <=. length []               
  *** QED 
filterLength p (x:xs) = undefined

The intermediate justification ? leqZero (length []) raises

error:
    * Couldn't match type `Int' with `()'
      Expected: Proof
        Actual: Int
    * In the second argument of `(<=.)', namely `length []'
      In the second argument of `(?)', namely
        `leqZero (length []) <=. length []'
      In the first argument of `(***)', namely
        `length (filter p []) ? filter p [] ==. length [] ? length [] ==. 0
           ? leqZero (length []) <=. length []'
   |
37 |   <=. length []

From my understanding, the justification should be optional just like in ==., but sometimes it may be neccesary.

Ah that's just a matter of the "parsing/precedence" see this

http://goto.ucsd.edu:8090/index.html#?demo=permalink%2F1694706430_580.hs

in fact, adding

infixl 3 <=.

to the file makes it work directly.

http://goto.ucsd.edu:8090/index.html#?demo=permalink%2F1694706535_584.hs