Доказательство ассоциативности сложения натуральных чисел c использованием бесформенного Scala

следующий код-Idris:

natAssociative : (a : Nat) -> (b : Nat) -> (c : Nat) -> (a + b) + c = a + (b + c)
natAssociative Z b c = the (b + c = b + c) refl
natAssociative (S k) b c = replace {P=x => S (k + b) + c = S x} (natAssociative k b c) refl

мне очень трудно перевести это на бесформенный. Я пробовал несколько разных кодировок, но я думаю, что это было самое многообещающее начало:

import scalaz.Leibniz._
import shapeless.{ HNil, Nat, Succ, Poly3 }
import shapeless.Nat._
import shapeless.ops.nat._

object natAssociative extends Poly3 {
  implicit def case0[B <: Nat, C <: Nat]: Case[_0, B, C] = at[_0, B, C] {
    case (Nat._0, b, c) => refl[Sum[B, C]#Out]
  }
  implicit def caseSucc[K <: Nat, B <: Nat, C <: Nat] = ???
}

у меня проблемы с индукцией и заставить Scala признать, что у нас есть 2 возможных случая для рекурсии. Есть ли трюк для кодирования этой части?

1 ответов


с определением Nat и Sum в бесформенном, вы ничего не можете доказать. Потому что Sum не является функцией, с такими же аргументами, мы можем иметь другой результат:

object Pooper {
  implicit def invalidSum: Sum[_1, _1] = new Sum[_1, _1] {
    type Out = _3
  }
}

но если мы определяем naturals и суммирование немного по-другому:

package plusassoc

import scala.language.higherKinds
import scalaz.Leibniz

sealed trait Nat {
  type Add[A <: Nat] <: Nat // 1.add(5)
}

case class Zero() extends Nat {
  type Add[A <: Nat] = A
}

case class Succ[N <: Nat]() extends Nat {
  type Add[A <: Nat] = Succ[N#Add[A]]
}

// a for aliases
package object a {
  // Equality on nats
  type ===[A <: Nat, B <: Nat] = Leibniz[Nothing, Nat, A, B]

  type Plus[A <: Nat, B <: Nat] = A#Add[B]

  type One = Succ[Zero]
  type Two = Succ[One]
  type Three = Succ[Two]
}

import a._

на AddPlus) теперь хорошо себя ведут функции уровня типа.


тогда мы можем написать доказательство ассоциативности Plus:

/*
  plus-assoc : ∀ n m p → (n + (m + p)) ≡ ((n + m) + p)
  plus-assoc zero m p = refl
  plus-assoc (suc n) m p = cong suc (plus-assoc n m p)
*/
trait PlusAssoc[N <: Nat, M <: Nat, P <: Nat] {
  val proof: Plus[N,Plus[M, P]] === Plus[Plus[N, M], P]
}

object PlusAssoc {
  implicit def plusAssocZero[M <: Nat, P <: Nat]: PlusAssoc[Zero, M, P] = new PlusAssoc[Zero, M, P] {
    val proof: Plus[M,P] === Plus[M,P] = Leibniz.refl
  }

  implicit def plusAssocSucc[N <: Nat, M <: Nat, P <: Nat](implicit
    ih: PlusAssoc[N, M, P]): PlusAssoc[Succ[N], M, P] = new PlusAssoc[Succ[N], M, P] {
      // For some reason scalac fails to infer right params for lift :(
      val proof: Succ[Plus[N,Plus[M, P]]] === Succ[Plus[Plus[N, M], P]] = Leibniz.lift[
        Nothing, Nothing,
        Nat, Nat,
        Succ,
        Plus[N, Plus[M, P]], Plus[Plus[N, M], P]
      ](ih.proof)
    }
}

и как мы полагаться на неявные преобразования, мы должны проверить, что scalac может действительно построить доказательство, используя наши "правила":

import plusassoc._
import plusassoc.a._
import plusassoc.PlusAssoc._

implicitly[PlusAssoc[One, Two, Three]].proof
res0: ===[Plus[One,Plus[Two,Three]],Plus[Plus[One,Two],Three]] = scalaz.LeibnizFunctions$$anon@7b2c4c00
// with plusassoc.a. prefix skipped