我想在haskell中为无限数字序列定义一个类型。我的想法是:
type MySeq = Natural -> Ratio Integer
但是,我还希望能够在类型级别定义序列的某些属性。一个简单的例子就是像这样的非递减序列。这可以用GHC的当前依赖型功能来实现吗?
编辑:我提出了以下想法:type PositiveSeq = Natural -> Ratio Natural
data IncreasingSeq = IncreasingSeq {
start :: Ratio Natural,
diff :: PositiveSeq}
type IKnowItsIncreasing = [Ratio Natural]
getSeq :: IncreasingSeq -> IKnowItsIncreasing
getSeq s = scanl (+) (start s) [diff s i | i <- [1..]]
当然,它基本上都是黑客攻击,实际上根本不是安全的。
答案 0 :(得分:1)
这并没有对类型做任何非常奇特的事情,但你可以改变你对自然序列的解释方式,以获得基本相同的保证。
我认为你正在考虑编辑问题的正确方向。考虑
data IncreasingSeq = IncreasingSeq (Integer -> Ratio Natural)
其中每个比率表示它与前一个数字相比增加了多少(从0开始)。
然后你可以提供单一功能
applyToIncreasing :: ([Ratio Natural] -> r) -> IncreasingSeq -> r
applyToIncreasing f (IncreasingSeq s) = f . drop 1 $ scanl (+) 0 (map (s $) [0..])
这应该让你以任何方式解构它,而不允许函数检查真实的结构。
你只需要一种方法来构建它:可能是fromList
只需对其进行排序,而insert
执行标准的有序插入。
我很难说这一点,但我不认为你会使用花哨的类型技巧获得任何东西:只有三个函数可能出错,而且它们很容易正确实现。该实现是隐藏的,因此使用它们的任何东西都是正确的,因为这些函数是正确的。只是不要导出IncreasingSeq
的数据构造函数。
我还建议考虑将[Ratio Natural]
作为基础代表。它简化了事情并保证序列中没有“间隙”(所以它保证是一个序列)。
如果您想要更高的安全性并且可以降低性能,可以使用data Nat = Z | S Nat
代替Natural
。
我会说,如果这是Coq或类似的语言,而不是Haskell我会更有可能建议做一些更高级的类型级别的东西(取决于你想要完成的东西),原因有两个:< / p>
在像Coq这样的系统中,您通常会证明有关代码的定理。因此,拥有某个属性所包含的类型级证明会很有用。由于Haskell并没有真正的内在方法来证明这些定理,因此效用减弱了。
另一方面,我们可以(有时)使用少量可信函数构建基本上必须具有我们想要的属性的数据类型隐藏的实现。在具有更多定理证明能力的系统(如Coq)的上下文中,与使用依赖类型(可能至少)相比,这可能更难说服该属性的定理证明者。但是,在Haskell中,我们首先没有这个问题。