使用 Liquid Haskell 检查有效令牌

Using Liquid Haskell to Check for Valid Tokens

我正在使用 liquid-haskell 做一些实验,看看我可以用它做些什么,但我遇到了一些困难。基本思想是我有一些功能需要访问令牌,该令牌会在经过一定时间后过期。我正在尝试查看如何使用 liquid-haskell 来确保在将令牌传递到我的函数之一之前检查令牌的有效性。我在下面创建了一个最小的工作版本来演示我的问题。当我在这个文件上 运行 liquid 时,我得到以下错误:

/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}

我似乎无法弄清楚为什么会出现此错误,而且我尝试的一切都失败了。有人可以帮我吗?

此外,我想将 currTime 函数替换为 time 包中的 getCurrentTime 函数。这样我就可以将令牌上的时间戳与当前时间进行比较。这意味着我的 isExpired 函数的类型为 Token -> IO Bool。甚至可以使用液体 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

谢谢!

这里有几个问题。

  1. 您试图将 currTime 定义为度量,但度量应该是函数。这应该被 LiquidHaskell 标记为错误。

  2. 您在制定 currTime 措施之前可能已经注意到这一点,但您目前无法在类型签名中引用顶级定义。我们可以通过将 currTime 作为参数传递给 isExpired 并向 ValidToken 类型添加一个参数来修复您的示例(这可能是您想要做的,因为token 是关于某个时间戳的)。这是我们演示页面上工作版本的link

最后,您当然可以重写代码以在 isValid 中使用 getCurrentTime,但您可能需要更改 ValidToken 的定义,因为当前时间永远不会逃脱 isValidHere's 我会怎么做。

我定义了一个名为 valid 的 "uninterpreted" 度量(无正文)并将 isExpired 的类型更改为 return IO {v:Bool | ((Prop v) <=> (not (valid t)))}。不幸的是,LiquidHaskell 无法验证 isExpired 的定义,因为我们还没有告诉它 valid 是什么意思。所以我们必须 assume isExpired 的类型,使其成为我们可信计算基础的一部分。我同意这个,因为它是一个小函数,而且是唯一需要假设的东西。