从Idris到Scala的通用加法器?

时间:2016-03-10 04:36:27

标签: scala shapeless idris

Type Driven Development with Idris提供以下通用加法器方法:

AdderType : (numArgs : Nat) -> Type
AdderType Z     = Int
AdderType (S k) = (next : Int) -> AdderType k

adder : (n : Nat) -> (acc : Int) -> AdderType n
adder Z acc     = acc
adder (S k) acc = \x => (adder k (x+acc))

示例:

-- expects 3 Int's to add, with a starting value of 0
*Work> :t (adder 3 0) 
adder 3 0 : Int -> Int -> Int -> Int

-- 0 (initial) + 3 + 3 + 3 == 9
*Work> (adder 3 0) 3 3 3
9 : Int

我猜测shapeless可以处理上述通用adder函数。

如果有或没有无形,可以用Scala编写?

2 个答案:

答案 0 :(得分:9)

更新:我将在下面留下我原来的实现,但这里有一个更直接的实现:

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
   //put here the html for the log in screen
} else {
   $response = '{status: false, message: "Please log in"}';
   echo json_encode($response);
}

然后:

import shapeless._

trait AdderType[N <: Nat] extends DepFn1[Int]

object AdderType {
  type Aux[N <: Nat, Out0] = AdderType[N] { type Out = Out0 }
  def apply[N <: Nat](base: Int)(implicit at: AdderType[N]): at.Out = at(base)

  implicit val adderTypeZero: Aux[Nat._0, Int] = new AdderType[Nat._0] {
    type Out = Int
    def apply(x: Int): Int = x
  }

  implicit def adderTypeSucc[N <: Nat](implicit
    atN: AdderType[N]
  ): Aux[Succ[N], Int => atN.Out] = new AdderType[Succ[N]] {
    type Out = Int => atN.Out
    def apply(x: Int): Int => atN.Out = i => atN(x + i)
  }
}

以下原始答案。

这是一个袖手旁观的Scala翻译:

scala> val at3 = AdderType[Nat._3](0)
at3: Int => (Int => (Int => Int)) = <function1>

scala> at3(3)(3)(3)
res8: Int = 9

然后:

import shapeless._

trait AdderType[N <: Nat] extends DepFn1[Int] {
  protected def plus(x: Int): AdderType.Aux[N, Out]
}

object AdderType {
  type Aux[N <: Nat, Out0] = AdderType[N] { type Out = Out0 }

  def apply[N <: Nat](base: Int)(implicit at: AdderType[N]): Aux[N, at.Out] =
    at.plus(base)

  private[this] case class AdderTypeZero(acc: Int) extends AdderType[Nat._1] {
    type Out = Int
    def apply(x: Int): Int = acc + x
    protected def plus(x: Int): Aux[Nat._1, Int] = copy(acc = acc + x)
  }

  private[this] case class AdderTypeSucc[N <: Nat, Out0](
    atN: Aux[N, Out0],
    acc: Int
  ) extends AdderType[Succ[N]] {
    type Out = Aux[N, Out0]
    def apply(x: Int): Aux[N, Out0] = atN.plus(acc + x)
    protected def plus(x: Int): Aux[Succ[N], Aux[N, Out0]] = copy(acc = acc + x)
  }

  implicit val adderTypeZero: Aux[Nat._1, Int] = AdderTypeZero(0)
  implicit def adderTypeSucc[N <: Nat](implicit
    atN: AdderType[N]
  ): Aux[Succ[N], Aux[N, atN.Out]] = AdderTypeSucc(atN, 0)
}

它更冗长,并且表示形式有点不同,以使Scala语法能够解决 - 我们的基本情况&#34;基本上是scala> val at3 = AdderType[Nat._3](0) at3: AdderType[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] { ... scala> at3(3)(3)(3) res0: Int = 9 而不是Int => Int,因为否则我无法找到避免在任何地方写Intapply的方法 - 但基本的想法是完全一样。

答案 1 :(得分:3)

如果您长途跋涉并且手上没有无形,那么您可以在纯Scala中执行此操作。对于那些不熟悉 shapeless 的人以及因某些原因不使用它的人来说,它会很有用。

首先,我们需要一些方法来迭代类型,即表示类型中的自然数。您可以使用任何嵌套类型,或者只定义一个带有一些数字别名的新类型:

sealed trait Nat

trait Zero extends Nat
trait Succ[N <: Nat] extends Nat

// enough for examples:
type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
type _4 = Succ[_3]
// etc...

当然,如果您经常使用_42_342923等类型,那么使用某些宏来获取现有Nat类型会更方便更多 - 从构建价值观来构建那些,但是对于我们的例子来说已经足够了。

现在,AdderType依赖函数类型非常简单:

// first we define the type which take a Nat type argument
trait AdderType[N <: Nat] {

  type Out
  def apply(i: Int): Out
}

// then we inductively construct its values using implicits
case object AdderType {

  // base case: N = _0
  implicit def zero:
      AdderType[_0] { type Out = Int } =
  new AdderType[_0] {

    type Out = Int
    def apply(i: Int): Out = i
  }

  // induction step: N -> Succ[N]
  implicit def succ[N <: Nat, NOut](
    implicit prev: AdderType[N] { type Out = NOut }
  ):  AdderType[Succ[N]] { type Out = Int => NOut } =
  new AdderType[Succ[N]] {

    type Out = Int => NOut
    def apply(i: Int): Out = k => prev(i + k)
  }
}

现在,要构造AdderType的实例并应用它,我们编写一个函数,它将N <: Nat作为类型参数并隐式构造AdderType[N]

def adder[N <: Nat](initial: Int)(
  implicit adderFunction: AdderType[N]
): adderFunction.Out = adderFunction(initial)

那就是:

scala> val add3Numbers = adder_[_3](0)
add3Numbers: Int => (Int => (Int => Int)) = <function1>

scala> add3Numbers(1)(2)(3)
res0: Int = 6

你可以看到纯粹的解决方案并不比使用无形的解决方案更大或更复杂(尽管后者为我们提供了现成的Nat和{{1} } types)。

一点点补充:如果(在一些更一般的情况下)你不想使用DepFn,这有时会导致问题,我也有一个没有它的解决方案。在这种特殊情况下,它并没有更好,但无论如何我都会表现出来。

关键点是为out类型添加另一个类型参数:adderFunction.Out,但不能将adder[N <: Nat, NOut]作为类型传递给N,因为我们需要写adder,想要推断(否则,重点是什么)。所以我们可以传递一个额外的值参数,这将有助于派生NOut类型:

N

要构建def adder[N <: Nat, NOut](n: NatVal[N])(initial: Int)( implicit adderFunction: AdderType[N] { type Out = NOut } ): NOut = adderFunction(initial) 我们不需要创建每个NatVal[N]类型的实例,我们可以使用一个小技巧:

Nat

现在,您可以使用它:

// constructing "values" to derive its type arg
case class NatVal[N <: Nat]()

// just a convenience function
def nat[N <: Nat]: NatVal[N] = NatVal[N]()

您可以看到它有效,但没有向我们展示实际类型。然而,当你有几个依赖他人的隐含时,这种方法可以更好地工作。类型成员。我的意思是

scala> val add3Numbers = adder(nat[_3])(0)
add3Numbers: this.Out = <function1>

scala> add3Numbers(1)(2)(3)
res1: this.Out = 6

而不是

def foo[AOut]()(implicit
  a: A { type Out = AOut},
  b: B { type In = AOut }
) ...

因为你无法在同一个参数列表中找到def foo()(implicit a: A, b: B { type In = a.Out } ) ...

您可以在Github上的repo找到完整的代码。