一个简单的情况,LiquidHaskell可以在类型“ Data.String”上工作而不能在类型“ Data.Text”上工作

时间:2019-02-04 18:32:13

标签: haskell liquid-haskell

问题

我很高兴与 LiquidHaskell 一起玩,但是,我不知道我需要修改我的原始Haskell代码到什么程度才能满足LiquidHaskell的要求。

这是一个简单的示例,说明Liquid的规范如何适用于 String 类型,而不适用于 Text 类型。

对于String类型,效果很好

示例

我定义了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无法评估元组的值。有什么建议吗?

2 个答案:

答案 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.packreverse是内射的。我对LH的了解还不足以告诉我们是否有可能实现这一目标。也许强迫它内联转换函数可以解决问题。简而言之,唯一的选择就是将值NF并在其上调用实际的==运算符–在这种特殊情况下可以很好地工作,但是对于非平凡的用例来说LH实际上是不可能的应该做的。