Scala-在类型级别上减去两个自然数

时间:2018-12-16 12:29:28

标签: scala types type-level-computation

我们可以在Scala中编码自然数的加法和乘法。但是,是否可以在类型级别上减去两个自然数?

我在Scala中半复制了以下自然数编码:

sealed trait  Natural {
  type Plus[That <: Natural] <: Natural
}

case object Zero extends Natural {
  override type Plus[That <: Natural] = That
}

case class Suc[Prev <: Natural](n: Prev) extends Natural {
  override type Plus[That <: Natural] = Suc[Prev#Plus[That]]
}

然后我自己添加乘法:

sealed trait Natural {
  type Plus[That <: Natural] <: Natural
  type Mult[That <: Natural] <: Natural
}

case object Zero extends Natural {
  override type Plus[That <: Natural] = That
  override type Mult[That <: Natural] = Zero.type
}

case class Suc[Prev <: Natural](n: Prev) extends Natural {
  override type Plus[That <: Natural] = Suc[Prev#Plus[That]]
  override type Mult[That <: Natural] = (Prev#Mult[That])#Plus[That]
}

似乎与后来发现的其他实现是一致的,并且可以正常工作:

implicitly[Nat5#Mult[Nat2] =:= Nat10]
implicitly[Nat4#Mult[Nat4] =:= Nat8#Mult[Nat2]]

在过去的几个小时中,我一直在尝试实现减法。 通过以下方法,如果您减去的一个数字(b中的a - b)是一个奇数,我似乎可以正确地减去两个数字:

sealed trait  Natural {
  type Previous <: Natural
  type Minus[That <: Natural] <: Natural
}

case object Zero extends Natural {
  override type Previous = Zero.type
  override type Minus[That <: Natural] = That
}

case class Suc[Prev <: Natural](n: Prev) extends Natural {
  override type Previous = Prev
  override type Minus[That <: Natural] = (That#Previous)#Minus[Prev]
}

以上内容利用了(N - M) = (N - 1) - (M - 1)这一事实。最终,递归步骤M将命中Zero.type,并返回相应的递归步骤N。 实际上,请注意,我的实现在给定步骤中转换为(N - M) = (M - 1) - (N - 1)。因为减法不是可交换的,所以这是不正确的。但是,由于此“交换”发生在每个递归步骤中,因此,如果要减去的数字为奇数,则它会取消。如果它是一个偶数,那么此实现将关闭一个。特别是,它比正确的数字小1:

implicitly[Nat10#Minus[Nat3] =:= Nat7]  // Compiles
implicitly[Nat9#Minus[Nat3] =:= Nat6]   // Compiles
implicitly[Nat8#Minus[Nat3] =:= Nat5]   // Compiles

implicitly[Nat10#Minus[Nat2] =:= Nat8]  // Does not compile, while:
implicitly[Nat10#Minus[Nat2] =:= Nat7]  // Compiles

implicitly[Nat5#Minus[Nat2] =:= Nat3]  // Does not compile, while:
implicitly[Nat5#Minus[Nat2] =:= Nat2]  // Compiles

要了解原因,请在纸上进行尝试,其中{/ {1}}用于奇数/正确的情况,m = Suc[Zero.type] (Nat1)用于错误的情况。无论哪种情况,数字m = Suc[Suc[Zero.type]] (Nat2)(如n都不重要)

无论如何,我确实觉得使用这种方法可能会走上正轨,但我被困住了。

  1. 您知道如何执行此操作吗?也许您可以指出我在类型级别上的减法实现?
  2. 也许这只能通过对自然数的负数进行编码来实现?

p.s。我不担心n - m中的m > n会发生什么。

用于完整介绍示例的有用信息:

n - m

1 个答案:

答案 0 :(得分:3)

由于减法的递归定义与第二个参数匹配,因此您可以定义:

sealed trait Natural {
  type ThisType <: Natural  
  type Previous <: Natural
  type Minus[That <: Natural] = That#SubtractThis[ThisType]
  type SubtractThis[That <: Natural] <: Natural
}

case object Zero extends Natural {
  type ThisType = Zero.type
  type Previous = Zero.type
  type SubtractThis[That <: Natural] = That
}

case class Suc[Prev <: Natural](n: Prev) extends Natural {
  type ThisType = Suc[Prev]
  type Previous = Prev
  type SubtractThis[That <: Natural] = Previous#SubtractThis[That#Previous]  
}