【问题标题】:Encoding "Less Than" with Haskell用 Haskell 编码“小于”
【发布时间】:2013-07-08 20:38:41
【问题描述】:

我希望一些 Haskell 专家可以帮助澄清一些事情。

是否可以以通常的方式定义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 关系 这样对于所有nm

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

但这不是我想要的。

谢谢!

兰吉特。

【问题讨论】:

  • foo :: exists n... – 真的吗?所以你想让foo返回它喜欢的任何类型,唯一的限制是它是“小于m”?这在 Haskell 中是不可能的(不仅仅是那样),这是正确的。还是您的意思是,foo 可以返回调用者请求的任何类型,只要它小于m
  • “some”似乎可以与该句子中的“any”互换。关键问题是:谁来决定它的类型?
  • 没有人决定,我只想要一个说明“输出是 some nat 严格小于输入”(没有说 what 这个数字是……)
  • 那么决定类型 取决于函数(或者如果你愿意的话,那家伙如何实现它)?
  • @MonadNewb 这是类型级编程,用于一些超狡猾的类型技巧。 Ranjit 在类型系统中编码整数而不是数据,这就是为什么LessThan 也需要在类型系统中。在您对 Haskell 非常有信心之前,忽略类型级编程是安全的。

标签: haskell gadt dependent-type singleton-type


【解决方案1】:

这是实现类似于您所询问的内容的一种方法。

自然

首先请注意,您将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 的小引理。引理说对于所有nn 小于S n。我们通过对n 的归纳来证明这一点。

lemma :: Nat n -> LessThan n (S n)
lemma Zero = LT1
lemma (Succ n) = LT3 (lemma n)

foo 的实现

现在我们可以编写问题中的代码了:

foo :: Nat m -> Foo m
foo (Succ n) = Foo n (lemma n)
foo Zero = foo Zero

【讨论】:

  • 仅供参考,还有另一种编码存在量化的方法;即。 exists n. Aforall r. (forall n. A -> r) -> r 编码
  • 解释得很好,很清楚;有时我想多次使用投票按钮。
  • 请注意,这不是您可以定义的唯一此类函数:: Nat m -> Foo m。比如你也可以定义lemma' :: Nat n -> LessThan Z (S n) ; lemma' Zero = LT1 ; lemma' (Succ n) = LT2 (lemma' n),得到foo' :: Nat m -> Foo m ; foo' (Succ n) = Foo Zero (lemma' n) ; foo' Zero = foo' Zero,同样符合定义。
  • 现在请注意,您可以定义一个函数:: Nat m -> [ Foo m ],它使用next :: Foo m -> Foo (S m) ; next (Foo n lt) = Foo (Succ n) (LT3 lt) ; range :: Nat m -> [ Foo m ] ; range Zero = [] ; range (Succ n) = foo' (Succ n) : map next (range n)确定所有此类 foo 函数的范围
猜你喜欢
  • 2011-03-19
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
相关资源
最近更新 更多