I am trying to do the first exercise in the LiquidHaskell case study on Lazy Queues
module Main where
main :: IO ()
main = putStrLn "hello"
{-@ type Nat = {v:Int | 0 <= v} @-}
{-@ die :: {v:String | false} -> a @-}
die x = error x
{-@ measure realSize @-}
realSize :: [a] -> Int
realSize [] = 0
realSize (_:xs) = 1 + realSize xs
{-@ data SList a = SL {
size :: Nat
, elems :: {v:[a] | realSize v = size}
}
@-}
data SList a = SL { size :: Int, elems :: [a]}
{-@ type NEList a = {v:SList a | 0 < size v} @-}
{-@ hd :: NEList a -> a @-}
hd (SL _ (x:_)) = x
hd _ = die "empty SList"
okList = SL 1 ["cat"]
okHd = hd okList
okHd
fails:
app/Main.hs:30:13-18: Error: Liquid Type Mismatch
Inferred type
VV : {VV : (SList [Char]) | VV == Main.okList}
not a subtype of Required type
VV : {VV : (SList [Char]) | 0 < size VV}
In Context
VV : {VV : (SList [Char]) | VV == Main.okList}
Main.okList
: (SList [Char])
From the error message, I'm pretty sure I'm not giving enough information to LH for it to "know" that okList
is non-empty, but I can't figure out how to fix it.
I tried telling it explicitly with a post-condition(?):
{-@ okList :: NEList a @-}
okList = SL 1 ["cat"]
But this doesn't work:
app/Main.hs:29:5: Error: Specified Type Does Not Refine Haskell Type for Main.okList
Haskell: Main.SList [GHC.Types.Char]
Liquid : forall a. Main.SList a
Your refined type of
okList
is not restricting the type. It restricted the size but loose the type fromString
toa
.Change
to
And it will work.
I have to admit that I don't know liquidhaskell very well so everything below may only be my wild guess:
The main reason you have to do this is you are defining
okList
separately with the default constructorSL
. The refined type ofSList
only promises thatsize v = realSize (elems v)
, the size of the list is inspected when you call the constructor, compared with the number literal and then discarded, not stored at (liquid) type level. So when you feedokList
tohd
, the only information available for reasoning aresize v = realSize (elems v)
(from the refined data type) andsize v >= 0
(size
is defined as aNat
),hd
won't know if it's positive.In
hd okList
, liquidhaskell may not be able to evaluate the expression and by doing so substituteokList
withSl 1 ["cat"]
and gain information about the size, so it can only make judgement depend on the refined type ofokList
it inferred (in this case,SList String
). One evidence ishd $ SL 1 ["cat"]
will work with no refined type.