在scala中使用扁平元组的高阶操作

Chr*_*imm 8 scala tuples higher-kinded-types

我最近遇到了一个问题.我试图以编译器友好的方式压扁"尾嵌套"元组,我想出了下面的代码:

implicit def FS[T](x: T): List[T] = List(x)
implicit def flatten[T,V](x: (T,V))(implicit ft: T=>List[T], fv: V=>List[T]) = 
  ft(x._1) ++ fv(x._2)
Run Code Online (Sandbox Code Playgroud)

以上代码适用于扁平元组,我称之为"尾嵌套",如下所示.

flatten((1,2)) -> List(1,2)
flatten((1,(2,3))) -> List(1,2,3)
flatten((1,(2,(3,4)))) -> List(1,2,3,4)
Run Code Online (Sandbox Code Playgroud)

但是,我试图让我的解决方案更加强大.考虑一个案例,我有一个这些高级"尾嵌套"元组的列表.

val l = List( (1,2), (1,(2,3)), (1,(2,(3,4))) )
Run Code Online (Sandbox Code Playgroud)

推断的类型签名就是这样List[(Int, Any)],这会对诸如以下操作造成问题map:

error: No implicit view available from Any => List[Int]

这个错误对我来说很有意义,因为flatten函数中我的递归隐式链的性质.但是,我想知道:有没有什么方法可以让我的方法使元组变得更加强大,以便更高阶的函数,比如map网格很好?

编辑:

正如Bask.ws指出的那样,产品特性提供了一个很好的解决方案的潜力.以下代码说明了这一点:

def flatten(p: Product): List[_] = p.productIterator.toList.flatMap {x => x match {
  case pr: Product => flatten(pr)
  case _ => List(x)
}}
Run Code Online (Sandbox Code Playgroud)

此新展平调用的结果类型始终为List [Any].如果有一种方法让编译器收紧这个边界,我的问题就解决了.与我原来的问题并行,是否有人知道是否有可能实现这一目标?

Bas*_*.ws 3

添加了UPD编译时失败解决方案

我有一种可能适合您的解决方案。前 3 个示例的类型在编译时解析:Int、Tuple2[Int, Int]、Tuple2[Int, Tuple2[Int, Int]]。对于您的列表示例,您有实际类型为 List[(Int, Any)] 的异构列表,并且您必须在运行时解析第二种类型,或者可以通过宏来完成。所以你可能想真正implicit def flatten[T](x: (T,Any))按照你的错误建议来写

这是快速解决方案。它给出了一些警告,但效果很好:

  implicit def FS[T](x: T): List[T] = List(x)

  implicit def FP[T](x: Product): List[T] = {
    val res = (0 until x.productArity).map(i => x.productElement(i) match {
      case p: Product => FP[T](p)
      case e: T => FS(e)
      case _ => sys.error("incorrect element")
    })
    res.toList.flatten
  }

  implicit def flatten[T](x: (T,Any))(implicit ft: T=>List[T], fp: Product =>List[T]) =
    ft(x._1) ++ (x._2 match {
      case p: Product => fp(p)
      case t: T => ft(t)
    })


  val l = List( (1,2), (1,(2,3)), (1,(2,(3,4))) )

  scala> l.map(_.flatten)
  res0: List[List[Int]] = List(List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
Run Code Online (Sandbox Code Playgroud)

UPD 我对问题进行了更多研究,并且找到了制作同质列表的简单解决方案,该解决方案可能在编译时失败。它是完全类型化的,没有 Any 和 match,看起来编译器现在可以正确解析嵌套隐式

  case class InfiniteTuple[T](head: T, tail: Option[InfiniteTuple[T]] = None) {
    def flatten: List[T] = head +: tail.map(_.flatten).getOrElse(Nil)
  }

  implicit def toInfiniteTuple[T](x: T): InfiniteTuple[T] = InfiniteTuple(x)

  implicit def toInfiniteTuple2[T, V](x: (T, V))(implicit ft: V => InfiniteTuple[T]): InfiniteTuple[T] =
    InfiniteTuple(x._1, Some(ft(x._2)))

  def l: List[InfiniteTuple[Int]] = List( (1,2), (1,(2,3)), (1,(2,(3,4)))) //OK

  def c: List[InfiniteTuple[Int]] = List( (1,2), (1,(2,3)), (1,(2,(3,"44"))))

  //Compile-time error
  //<console>:11: error: No implicit view available from (Int, (Int, java.lang.String)) => InfiniteTuple[Int]
Run Code Online (Sandbox Code Playgroud)

然后你可以实现任何你想要的展平。例如上面的一个:

scala> l.map(_.flatten)
res0: List[List[Int]] = List(List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
Run Code Online (Sandbox Code Playgroud)