如何使用LiquidHaskell指定在非空数据结构上运行的函数?

时间:2016-02-22 01:19:17

标签: haskell smt liquid-haskell

我正在尝试在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失败:

 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提供足够的信息,因为它知道&#34;知道&#34; 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

1 个答案:

答案 0 :(得分:4)

您的精炼类型okList不限制类型。它限制了尺寸,但将类型从String扩展到a

更改

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

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

它会起作用。

我必须承认我不太了解liquidhaskell,所以下面的所有内容可能只是我的猜测:

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

hd okList中,liquidhaskell可能无法评估表达式,并通过okList替换Sl 1 ["cat"]并获取有关大小的信息,因此只能根据推断的精确类型okList(在本例中为SList String)。一个证据是hd $ SL 1 ["cat"]将无法使用精炼类型。