I am doing some experimentation using liquid-haskell to see what kinds of neat things I can do with it and I have hit a bit of a wall. The basic idea is that I have some functions that require an access token that expires after a certain amount of time has passed. I am trying to see how I might use liquid-haskell to ensure that the token is checked for validity before passing it into one of my functions. I have created a minimal working version below that demonstrates my problem. When I run liquid on this file I get the following error:
/tmp/liquidTest.hs:18:17-42: Error: Liquid Type Mismatch
18 | isExpired tok = currTime >= expiration tok
^^^^^^^^^^^^^^^^^^^^^^^^^^
Inferred type
VV : {VV : GHC.Types.Bool | Prop VV <=> Main.currTime >= ?a}
not a subtype of Required type
VV : {VV : GHC.Types.Bool | Prop VV <=> currTime >= expiration tok}
In Context
Main.currTime := Data.Time.Clock.UTC.UTCTime
tok := Main.Token
?a := {?a : Data.Time.Clock.UTC.UTCTime | ?a == expiration tok}
I cannot seem to figure out why this error is showing up and everything I have tried has failed. Could someone please help me?
Also, I would like to have the currTime function replaced with the getCurrentTime function in the time package. That way I can compare the timestamp on the token with the current time. That would mean that my isExpired function would be of type Token -> IO Bool. Would that even be possible using liquid haskell?
import Data.Time
import Language.Haskell.Liquid.Prelude
{-@ data Token = Token
(expiration :: UTCTime)
@-}
data Token = Token
{ expiration :: UTCTime
} deriving Show
{-@ measure currTime :: UTCTime @-}
currTime :: UTCTime
currTime = UTCTime (ModifiedJulianDay 57614) 83924.978297
{-@ isExpired :: t:Token -> {v:Bool | ((Prop v) <=> (currTime >= expiration t))} @-}
isExpired :: Token -> Bool
isExpired tok = currTime >= expiration tok
{-@ type ValidToken = {t:Token | currTime < expiration t} @-}
{-@ showToken :: ValidToken -> String @-}
showToken :: Token -> String
showToken tok = show tok
main :: IO ()
main = do
ct <- getCurrentTime
let tok = Token ct
print currTime
case isExpired tok of
True -> putStrLn "The token has expired!"
False -> putStrLn $ showToken tok
Thanks!
There are a couple problems here.
You're trying to define
currTimeas a measure but measures are supposed to be functions. This should be flagged as an error by LiquidHaskell.You may have noticed this before you made
currTimea measure, but you can't currently refer to top-level definitions in a type signature. We can fix your example by passingcurrTimeas a parameter toisExpired, and by adding a parameter to theValidTokentype (which is probably what you want to do anyway since the validity of the token is with respect to some timestamp). Here's a link to the working version on our demo page.Finally, you certainly can rewrite the code to use
getCurrentTimeinsideisValid, though you'll probably need to change the definition ofValidTokensince the current time never escapesisValid. Here's how I would do it.I define an "uninterpreted" measure (no body) called
validand changeisExpired's type to returnIO {v:Bool | ((Prop v) <=> (not (valid t)))}. Unfortunately, LiquidHaskell can't verify the definition ofisExpiredbecause we haven't told it whatvalidmeans. So we have toassumethe type forisExpired, making it part of our trusted computing base. I'm ok with this because it's a small function, and it's the only thing that needs to be assumed.