【问题标题】:How to write a log2 function in Liquid Haskell如何在 Liquid Haskell 中编写 log2 函数
【发布时间】:2019-11-11 18:26:08
【问题描述】:

我正在尝试从book 学习 Liquid Haskell。 为了测试我的理解,我想编写一个函数log2,它接受形式为 2^n 的输入并输出 n。

我有以下代码:

powers :: [Int]
powers = map (2^) [0..]

{-@ type Powers = {v:Nat | v elem powers } @-}
{-@ log2 :: Powers -> Nat @-}
log2 :: Int -> Int
log2 n
 | n == 1 = 0
 | otherwise = 1 + log2 (div n 2)

但是在执行这段代码时出现了一些奇怪的错误,即“Sort Error in Refinement”。我无法理解并解决此错误。

任何帮助将不胜感激。

编辑:来自 Liquid Haskell 书:

谓词要么是原子谓词,通过比较获得 两个表达式,或者,谓词函数对列表的应用 论据...

在 Liquid Haskell 逻辑语法中,允许的谓词之一是:e r e 其中r 是原子二元关系(函数只是一种特殊的关系)。

此外,在教程中,他们将Even 子类型定义为: {-@ type Even = {v:Int | v mod 2 == 0 } @-}

基于此,我认为elem 应该可以工作。

但现在正如@ThomasM.DuBuisson 指出的那样,我想改写我自己的elem',以避免混淆。

elem' :: Int -> [Int] -> Bool
elem' _ [] = False
elem' e (x:xs)
 | e==x = True
 | otherwise = elem' e xs

现在,据我了解,为了能够将此 elem' 用作谓词函数,我需要将其提升为度量。所以我添加了以下内容:

{-@ measure elem' :: Int -> [Int] -> Bool @-}

现在我在Powers 的类型定义中将elem 替换为elem'。但我仍然得到与上一个相同的错误。

【问题讨论】:

  • elem 是一个变量,而不是中缀运算符。另外,elem 是在哪里定义的?
  • @ThomasM.DuBuisson 呃,Prelude?
  • @JosephSible 但是液体haskell的前奏是否包含elem的提升逻辑版本是这里真正的问题。
  • 我问是因为(诚然过时的)“尝试液体 Haskell”在线不知道elem 是什么所以我怀疑@kishlaya 必须为使用液体 Haskell 提供合适的定义定义Powers 并细化类型检查log2
  • @ThomasM.DuBuisson 我已经用相关细节编辑了我的问题。

标签: haskell types liquid-haskell


【解决方案1】:

@TomMD 指的是“反射”的概念,它允许您将 Haskell 函数(在某些限制下)转换为改进,例如看到这些帖子:

https://ucsd-progsys.github.io/liquidhaskell-blog/tags/reflection.html

很遗憾,还没有时间用这些材料更新教程。

例如,您可以如下所示描述 log2/pow2:

https://ucsd-progsys.github.io/liquidhaskell-blog/tags/reflection.html

http://goto.ucsd.edu/liquid/index.html#?demo=permalink%2F1573673688_378.hs

特别是你可以写:

{-@ reflect log2 @-}
log2 :: Int -> Int
log2 1 = 0
log2 n = 1 + log2 (div n 2) 

{-@ reflect pow2 @-}
{-@ pow2 :: Nat -> Nat @-}
pow2 :: Int -> Int
pow2 0 = 1
pow2 n = 2 * pow2 (n-1)

然后您可以在编译时“检查”以下内容是否正确:

test8 :: () -> Int
test8 _ = log2 8 === 3

test16 :: () -> Int
test16 _ = log2 16 === 4

test3 :: () -> Int
test3 _ = pow2 3 === 8

test4 :: () -> Int
test4 _ = pow2 4 === 16 

但是,类型检查器会拒绝以下内容

test8' :: () -> Int
test8' _ = log2 8 === 5     -- type error

最后,你可以证明以下定理与log2pow2相关

{-@ thm_log_pow :: n:Nat -> { log2 (pow2 n) == n } @-}

“证明”是“对 n 进行归纳”,意思是:

thm_log_pow :: Int -> () 
thm_log_pow 0 = ()
thm_log_pow n = thm_log_pow (n-1)

回到你原来的问题,你可以将isPow2定义为:

{-@ reflect isEven @-}
isEven :: Int -> Bool
isEven n = n `mod` 2 == 0

{-@ reflect isPow2 @-}
isPow2 :: Int -> Bool
isPow2 1 = True
isPow2 n = isEven n && isPow2 (n `div` 2) 

您可以通过验证以下内容来“测试”它是否正确:

testPow2_8 :: () -> Bool
testPow2_8 () = isPow2 8 === True 

testPow2_9 :: () -> Bool
testPow2_9 () = isPow2 9 === False 

最后,通过给pow2 提供精炼类型:

{-@ reflect pow2 @-}
{-@ pow2 :: Nat -> {v:Nat | isPow2 v} @-}
pow2 :: Int -> Int
pow2 0 = 1
pow2 n = 2 * pow2 (n-1)

希望这会有所帮助!

【讨论】:

  • 哦,天哪,它可以是微不足道的反映。我有一个错误的假设。
猜你喜欢
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
  • 1970-01-01
  • 2010-12-03
  • 2018-03-02
相关资源
最近更新 更多