3

我正在尝试在关于懒惰队列的 LiquidHaskell 案例研究中做第一个练习

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失败:

 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])

从错误消息中,我很确定我没有向 LH 提供足够的信息以使其“知道”okList非空,但我不知道如何解决它。

我尝试用后置条件(?)明确地告诉它:

{-@ okList :: NEList a @-}
okList = SL 1 ["cat"]

但这不起作用:

 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
4

1 回答 1

4

您的精炼类型okList不限制类型。String它限制了大小,但将类型从到松散了a

改变

{-@ okList :: NEList a @-}
okList = SL 1 ["cat"]

{-@ okList :: NEList String @-}
okList = SL 1 ["cat"]

它会起作用。


我不得不承认我不太了解liquidhaskell,所以下面的一切可能只是我的猜测:

您必须这样做的主要原因是您okList使用默认构造函数单独定义SL。only的精炼类型SList承诺,size v = realSize (elems v)当您调用构造函数时,会检查列表的大小,与数字文字进行比较然后丢弃,而不是存储在(液体)类型级别。因此,当您okList输入 时hd,可用于推理的唯一信息是size v = realSize (elems v)(来自精炼数据类型)和size v >= 0size定义为 a Nat),hd不知道它是否为正。

hd okList中,liquidhaskell 可能无法评估表达式,并通过这样做替换okListSl 1 ["cat"]获得有关大小的信息,因此它只能根据okList推断的精炼类型(在本例中为SList String)做出判断。一个证据是hd $ SL 1 ["cat"]在没有精炼类型的情况下也可以工作。

于 2016-02-22T05:38:57.097 回答