【问题标题】:Scala recursive type and type constructor implementationScala递归类型和类型构造函数实现
【发布时间】:2017-05-20 21:18:12
【问题描述】:

我有一种情况,我需要一个可以接受类型的方法:

Array[Int]
Array[Array[Int]]
Array[Array[Array[Int]]]
Array[Array[Array[Array[Int]]]]
etc...

我们将这种类型的 RAI 称为“递归整数数组”

def make(rai: RAI): ArrayPrinter = { ArrayPrinter(rai) }

其中 ArrayPrinter 是一个使用 RAI 初始化并遍历整个 rai 的类(假设它打印此 Array[Array[Int]] 中的所有值)

val arrayOfArray: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4))
val printer: ArrayPrinter[Array[Array[Int]]] = make(arrayOfArray)
printer.print_! // prints "1, 2, 3, 4" 

它还可以返回原始的 Array[Array[Int]] 而不会丢失任何类型信息。

val arr: Array[Array[Int]] = printer.getNestedArray() 

如何在 Scala 中实现这一点?

【问题讨论】:

标签: scala higher-kinded-types type-constructor recursive-type


【解决方案1】:

让我们首先关注类型。根据您的定义,T 类型应该作为ArrayPrinter 的参数进行类型检查,它是否被以下类型函数接受:

def accept[T]: Boolean =
  T match { // That's everyday business in agda
    case Array[Int] => true
    case Array[X]   => accept[X]
    case _          => false
  }

在 Scala 中,您可以使用隐式解析对该类型函数进行编码:

trait RAI[T]

object RAI {
  implicit val e0: RAI[Array[Int]] = null
  implicit def e1[T](implicit i: RAI[T]): RAI[Array[T]] = null
}

case class ArrayPrinter[T: RAI](getNestedArray: T) // Only compiles it T is a RAI

要打印东西,最简单的解决方案是将rai: T 视为rai: Any:

def print_!: Unit = {
  def print0(a: Any): Unit = a match {
    case a: Int      => println(a)
    case a: Array[_] => a.foreach(print0)
    case _           => ???
  }
}

您也可以喜欢并使用类型类编写print_!,但这可能会比上面的效率更低并且需要更多的时间来编写......留给读者作为练习;-)

【讨论】:

    【解决方案2】:

    这通常是通过定义一个抽象类来完成的,该类包含您想要的与此递归类型相关的所有功能,但实际上并不接受任何构造函数参数。相反,它的所有方法都将(至少一个)类型作为参数。典型的例子是Ordering。定义此类的一个或多个隐式实现,然后在您需要使用它的任何时候,将其作为隐式参数接受。对应的例子是List's sorted method。

    在你的情况下,这可能看起来像:

    abstract class ArrayPrinter[A] {
      def mkString(a: A): String
    }
    implicit object BaseArrayPrinter extends ArrayPrinter[Int] {
      override def mkString(x: Int) = x.toString
    }
    class WrappedArrayPrinter[A](wrapped: ArrayPrinter[A]) extends ArrayPrinter[Array[A]] {
      override def mkString(xs: Array[A]) = xs.map(wrapped.mkString).mkString(", ")
    }
    implicit def makeWrappedAP[A](implicit wrapped: ArrayPrinter[A]): ArrayPrinter[Array[A]] = new WrappedArrayPrinter(wrapped)
    
    def printHello[A](xs: A)(implicit printer: ArrayPrinter[A]): Unit = {
      println("hello, array: " + printer.mkString(xs))
    }
    

    这往往比让 RAIOps 类(或 ArrayPrinter)接受一个对象作为其构造函数的一部分要干净一些。这通常会导致更多的“装箱”和“拆箱”、复杂的类型签名、奇怪的模式匹配等。

    它还有一个额外的好处是更容易扩展。如果稍后其他人有理由想要为Set[Int] 实现 ArrayPrinter,他们可以在本地将其定义到他们的代码中。我已经多次定义了自定义Ordering。

    【讨论】:

      猜你喜欢
      • 2022-08-03
      • 1970-01-01
      • 2021-10-23
      • 1970-01-01
      • 2011-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-11
      相关资源
      最近更新 更多