refinement types for haskell
TRANSCRIPT
Refinement types for Haskell
Martin Ockajak from Zürich
Software Engineer
@martin_ockajak
Outline
● Motivation
● Refinement types
● Liquid Haskell
● Practical considerations
Motivation
Standard type system
● Allows expressing certain properties of programs● Type safety
● Verifiable without running the program● Static type checking
● Integrated with the compilation
● Testing still needed
● Can we do better ?
Possible improvements
● Prevent more programming errors● Division by zero
● Missing keys in maps
● Infinite loops
● Express properties of programs in greater detail
● Keep the ability to automatically verify type safety● Verification must be a decidable problem
● No proofs by the programmer required
Refinement types
Refinement types
● Consist of● Type
● Standard or refinement
● Predicate● Propositional logic
● Can describe valid inputs and outputs of functions● Type safe if the predicate is valid for all inputs
Predicate
● Boolean operators● && , || , not , => , <=> , true , false
● Arithmetic operators● + , - , * , / , mod
● Relations● == , /= , < , > , <= , >=
Liquid Haskell
Liquid Haskell
● Static refinement type verifier● Completely automatic
● Translates refinement types into verification conditions● Satisfiability modulo theories formulas
● Uses an SMT solver to verify those conditions● Without executing the program or enumerating inputs
● Project at University of California - San Diego● http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/about/
Defining refinement types● Positive is a subtype of NonZero
● Positive values are a subset of NonZero values
{-@ type NonZero = {v: Int | v /= 0 } @-}{-@ type Positive = {v: Int | v > 0 } @-}{-@ type Odd = {v: Int | v mod 2 == 1 } @-}
{-@ one :: NonZero @-}{-@ one :: Positive @-}{-@ one :: Odd @-}one :: Intone = 1
{-@ odds :: [Odd] @-}odds :: [Int]odds = [1, 3, 7]
Refining function results
{-@ two :: {v: Int | v mod 2 == 0 } @-}{-@ one, two :: NonZero @-}two :: Inttwo = 1 + 1
{-@ size :: [a] -> {v: Int | v >= 0 } @-}size :: [a] -> Intsize [] = 0size (x:xs) = 1 + size xs
{-@ positive :: n:Int -> { v: Bool | Prop v <=> n > 0 } @-}positive :: Int -> Boolpositive n = n > 0
Refining function arguments
{-@ crash :: {v: String | false } -> a @-}crash :: String -> acrash message = error message
{-@ divide :: Int -> NonZero -> Int @-}divide :: Int -> Int -> Intdivide n 0 = crash "division by zero"divide n d = n `div` d
correctDivide :: IntcorrectDivide = divide 1 1
incorrectDivide :: IntincorrectDivide = divide 1 0
Defining predicates
{-@ predicate Positive N = N > 0 @-}{-@ predicate Even N = N mod 2 == 0 @-}{-@ predicate PositiveOdd N = Positive N && not Even N @-}
{-@ type Even = { v: Int | Even v } @-}
{-@ three :: { v: Int | PositiveOdd v || v == 4 } @-}three :: Intthree = 5 - 2
Measure functions● Can be used inside refinement type definitions● Single expression for every data constructor
● Propositional logic only
data List a = Emp | (:::) a (List a)
{-@ measure len @-}len :: List a -> Intlen Emp = 0len (x:::xs) = 1 + len xs
{-@ first :: {v: List a | len v > 0 } -> a @-}first Emp = crash "empty list"first (x:::xs) = x
Refining data types● Parametrized type alias used to specify list length
data Triple a = Triple (List a)
{-@ type ListN a N = {v: List a | len v == N} @-}
{-@ data Triple a = Triple (ListN a 3) @-}
correctTriple = Triple (1 ::: (2 ::: (3 ::: Emp)))
Inline functions and assumptions● Inline functions can be used inside measures● Assumptions allow describing non-verifiable functions
{-@ inline increment2 @-}increment2 :: Int -> Intincrement2 n = n + 2
{-@ measure doubleLen @-}doubleLen :: List a -> IntdoubleLen Emp = 0doubleLen (x:::xs) = increment2 (doubleLen xs)
{-@ assume abs :: (Num a) => a -> {v: a | v > 0 } @-}
Recursion
{-@ type NonNegative a = {v: a | v >= 0 } @-}{-@ type Natural a = {v: a | v > 0 } @-}
{-@ fact :: (Integral a) => NonNegative a -> Natural a @-}fact :: (Integral a) => a -> afact 0 = 1fact n = n * fact (n – 1)
correctFact = fact 3
incorrectFact = fact (-1)
Practical considerations
Practicality – Liquid Haskell
● Compatible with several SMT solvers
● Incremental checking support
● Decent documentation
● Still experimental
Thank you :-)