我很高兴与 LiquidHaskell 一起玩,但是,我不知道我需要修改我的原始Haskell代码到什么程度才能满足LiquidHaskell的要求。
这是一个简单的示例,说明Liquid的规范如何适用于 String 类型,而不适用于 Text 类型。
我定义了Liquid类型,我们说元组的值不能相同:
{-@ type NoRouteToHimself = {v:(_, _) | (fst v) /= (snd v)} @-}
然后,对于 String 类型规范,它的工作原理如下所示:
{-@ strOk :: NoRouteToHimself @-}
strOk :: (String, String)
strOk = ("AA", "AB")
LiquidHaskel输出>> 结果:安全
{-@ strBad :: NoRouteToHimself @-}
strBad :: (String, String)
strBad = ("AA", "AA")
LiquidHaskel输出>> 结果:不安全
到目前为止,让我们为Text类型定义相同的功能。
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as Tx
{-@ foo :: NoRouteToHimself @-}
foo :: (Tx.Text, Tx.Text)
foo = ("AA", "AB")
预期结果:结果:安全
LiquidHaskell输出:结果:UNSAFE
..Example.hs:102:3-5: Error: Liquid Type Mismatch
102 | foo = ("AA", "AB")
^^^
Inferred type
VV : {v : (Data.Text.Internal.Text, Data.Text.Internal.Text) | x_Tuple22 v == ?a
&& x_Tuple21 v == ?b
&& snd v == ?a
&& fst v == ?b}
not a subtype of Required type
VV : {VV : (Data.Text.Internal.Text, Data.Text.Internal.Text) | fst VV /= snd VV}
In Context
?b : Data.Text.Internal.Text
?a : Data.Text.Internal.Text
显然,在这种情况下,LiquidHaskell无法评估元组的值。有什么建议吗?
答案 0 :(得分:6)
经过一番摸索,我发现了一种可以执行此操作的方法。我不知道保留NoRouteToHimself
多态性的方法,但是至少有一种谈论Data.Text
对象相等的方法。
该技术是要引入表示量度。也就是说,Text
实际上只是表示 String
的一种奇特方式,因此我们原则上应该能够在{{1 }}对象。因此,我们引入一种措施来获取String
表示的内容:
Text
当我们从Text
构造{-@ measure denot :: Tx.Text -> String @-}
时,我们需要说Text
的表示法是我们传入的String
(这对插入性进行编码, Text
扮演相反的角色。
String
现在,当我们想比较LH中不同denot
的相等性时,我们将比较它们的符号。
{-@ assume fromStringL :: s:String -> { t:Tx.Text | denot t == s } @-}
fromStringL = Tx.pack
现在我们可以通过示例了:
Text
要在LH中使用{-@ type NoRouteToHimself = v:(_,_) : denot (fst v) /= denot (snd v) @-}
的其他功能,则需要为这些功能指定名称。这是一些工作,但我认为这是值得做的事情。
我很好奇是否有办法使这种处理方式更具多态性和可重用性。我还想知道我们是否可以重载LH的平等概念,从而不必经历{-@ foo :: NoRouteToHimself @-}
foo :: (Tx.Text, Tx.Text)
foo = (fromStringL "AA", fromStringL "AB")
。有很多东西要学。
答案 1 :(得分:3)
Liquid Haskell通过利用原始的Haskell构造函数来工作。 String
代码是糖
{-@ strOk :: NoRouteToHimself @-}
strOk :: (String, String)
strOk = (,) ('A':'A':[]) ('A':'B':[])
,Liquid Haskell知道如何解散/递归那些构造函数。但是Data.Text
并不是用Haskell构造函数定义的,而是使用了一个不透明的转换函数– -XOverloadedStrings
扩展插入了它:
{-@ foo :: NoRouteToHimself @-}
foo :: (Tx.Text, Tx.Text)
foo = (Tx.pack "AA", Tx.pack "AB")
在这里,Liquid Haskell不知道Tx.pack
是如何工作的,它是否产生任何可解构的输出。一个也无法成功的简单示例是(没有-XOverloadedStrings
)
{-@ foo :: NoRouteToHimself @-}
foo' :: (String, String)
foo' = (reverse "AA", reverse "AB")
要执行此操作,LH至少需要知道Tx.pack
和reverse
是内射的。我对LH的了解还不足以告诉我们是否有可能实现这一目标。也许强迫它内联转换函数可以解决问题。简而言之,唯一的选择就是将值NF并在其上调用实际的==
运算符–在这种特殊情况下可以很好地工作,但是对于非平凡的用例来说LH实际上是不可能的应该做的。