是否可以通常的方式定义Nat
(通过@dorchard Singleton types in Haskell)
data S n = Succ n
data Z = Zero
class Nat n
instance Nat Z
instance Nat n => Nat (S n)
(或其某些变体)然后定义LessThan
关系
如forall n
和m
LessThan Z (S Z)
LessThan n m => LessThan n (S m)
LessThan n m => LessThan (S n) (S m)
然后编写一个类似的函数:
foo :: exists n. (LessThan n m) => Nat m -> Nat n
foo (S n) = n
foo Z = foo Z
我明确地希望在foo
的输出类型中使用“LessThan”,
我意识到人们当然可以写出像
foo :: Nat (S n) -> Nat n
但那不是我追求的目标。
谢谢!
兰吉特。
答案 0 :(得分:17)
这是实现类似于你所要求的东西的一种方法。
首先请注意,您将Nat
定义为类,然后将其用作类型。我认为将它作为一种类型是有意义的,所以让我们这样定义它。
data Z
data S n
data Nat n where
Zero :: Nat Z
Succ :: Nat n -> Nat (S n)
我们还可以将LessThan
定义为类型。
data LessThan n m where
LT1 :: LessThan Z (S Z)
LT2 :: LessThan n m -> LessThan n (S m)
LT3 :: LessThan n m -> LessThan (S n) (S m)
请注意,我只是将您的三个属性转换为数据构造函数。这种类型的想法是LessThan n m
类型的完全标准化值证明n
小于m
。
现在你问:
foo :: exists n. (LessThan n m) => Nat m -> Nat n
但Haskell中不存在。相反,我们可以为foo
定义数据类型:
data Foo m where
Foo :: Nat n -> LessThan n m -> Foo m
请注意n
在这里有效地存在量化,因为它显示在数据构造函数Foo
的参数中,但不会显示在其结果中。现在我们可以说明foo
:
foo :: Nat m -> Foo m
在我们能够从问题中实现示例之前,我们必须证明LessThan
的一个小问题。引理说n
对于所有S n
都小于n
。我们通过n
上的归纳证明了这一点。
lemma :: Nat n -> LessThan n (S n)
lemma Zero = LT1
lemma (Succ n) = LT3 (lemma n)
现在我们可以编写问题的代码:
foo :: Nat m -> Foo m
foo (Succ n) = Foo n (lemma n)
foo Zero = foo Zero