コンテキスト: Haskell クイックソートの実装に関するいくつかのプロパティを証明しています。次のコードは、非決定論的permute
関数を定義するために必要なすべてです。Haskell の基本型LList
ではなく、使用していることに注意してください。[]
問題の領域はpermute_LCons_expansion_correct
定理です (定理というほどのものではありません)。
-- Data. A list is either `LNil` or constructed with a `vhead` element and a
-- `vtail` list.
data LList a = LNil | LCons {vhead :: a, vtail :: LList a}
-- Function. Append two lists.
{-@ reflect lappend @-}
lappend :: LList a -> LList a -> LList a
lappend LNil ys = ys
lappend (LCons x xs) ys = LCons x (lappend xs ys)
-- Function. Construct a list with 1 element.
{-@ reflect llist1 @-}
llist1 :: a -> LList a
llist1 x = LCons x LNil
-- Function. Construct a list with 2 elements.
{-@ reflect llist2 @-}
llist2 :: a -> a -> LList a
llist2 x y = llist1 x `lappend` llist1 y
-- Function. Map a list-function over a list and concatenate the resulting
-- lists (i.e. list-monadic bind).
{-@ reflect lbind @-}
lbind :: LList a -> (a -> LList b) -> LList b
lbind LNil f = LNil
lbind (LCons x xs) f = f x `lappend` lbind xs f
-- Function. Map a function over a list.
{-@ reflect lmap @-}
lmap :: (a -> b) -> (LList a -> LList b)
lmap f LNil = LNil
lmap f (LCons x xs) = LCons (f x) (lmap f xs)
{-@ reflect lmap2 @-}
-- Function. Map a binary function over two lists (zipping).
lmap2 :: (a -> b -> c) -> LList a -> LList b -> LList c
lmap2 f xs ys = lbind xs (\x -> lbind ys (\y -> llist1 (f x y)))
-- Function. Nondeterministically split a list into two sublists.
{-@ reflect split @-}
split :: LList a -> LList (LList a, LList a)
split LNil = llist1 (LNil, LNil)
split (LCons x xs) =
split xs
`lbind` \(ys, zs) ->
llist1 (LCons x ys, zs) `lappend` llist1 (ys, LCons x zs)
-- Function. Nondeterministically permute a list.
{-@ reflect permute @-}
permute :: LList a -> LList (LList a)
permute LNil = llist1 LNil
permute (LCons x xs) =
split xs `lbind` \(ys, zs) ->
lmap2
(\ys' zs' -> ys' `lappend` llist1 x `lappend` zs')
(permute ys)
(permute zs)
-- Function. The expanded form of `permute` on an `LCons`.
{-@ reflect permute_LCons_expansion @-}
permute_LCons_expansion :: a -> LList a -> LList (LList a)
permute_LCons_expansion x xs =
split xs
`lbind` ( \(ys, zs) ->
lmap2
(\ys' zs' -> ys' `lappend` llist1 x `lappend` zs')
(permute ys)
(permute zs)
)
-- Theorem. `permute_LCons_expansion` corresponds to `permute` on an `LCons`.
{-@
permute_LCons_expansion_correct :: x:a -> xs:LList a ->
{permute (LCons x xs) = permute_LCons_expansion x xs}
@-}
permute_LCons_expansion_correct :: a -> LList a -> Proof
permute_LCons_expansion_correct x xs =
permute (LCons x xs)
==. split xs
`lbind` ( \(ys, zs) ->
lmap2
(\ys' zs' -> ys' `lappend` llist1 x `lappend` zs')
(permute ys)
(permute zs)
)
==. permute_LCons_expansion x xs
*** QED
では、中央の式はケースの本体であり、 の定義でもあるpermute_LCons_expansion_correct
ため、方程式は正しいはずです。ただし、コンパイルすると、次の LH エラーが発生します。LCons
permute
permute_LCons_expansion
❯ cabal build
Build profile: -w ghc-8.10.3 -O1
{{I have removed project files info}}
**** LIQUID: UNSAFE ************************************************************
src/Test.hs:83:9: error:
Liquid Type Mismatch
.
The inferred type
VV : (Test.LList (Test.LList a))
.
is not a subtype of the required type
VV : {VV : (Test.LList (Test.LList a)) | permute (LCons x xs) == VV}
.
in the context
xs : (Test.LList a)
x : a
|
83 | ==. split xs
| ^^^^^^^^...
LH が暗黙の等価性を認識しないのはなぜですか? (==.)
fromLanguage.Haskell.Equational
ではなくfromを使用していることに注意してくださいLanguage.Haskell.ProofCombinators
。
構成:
- ghc バージョン 8.10.3
- LiquidHaskell バージョン 0.8.6.0
- カバル バージョン 3.2.0.0