如何在函数中平嵌套元组参数?

How to flat nest tuple parameter in function?

我有一个需要参数 (Int, (Int, Int)) => Int 的函数 g 和一个平面函数 f0 (Int, Int, Int) => Int

我想构造一个函数ft,它可以将g的参数扁平化为f0。 这是示例:

val f0: ((Int, Int, Int)) => Int = (x: (Int, Int, Int)) => {
  x._1 + x._2 + x._3
}

def g(f: ((Int, (Int, Int))) => Int): Int = f(1,(2,3))

def ft(f: ((Int, Int, Int)) => Int): ((Int, (Int, Int))) => Int = (p: (Int, (Int, Int)))  => {
  f(p._1, p._2._1, p._2._2)
  
}

// invoke it 
g(ft(f0))

但是我有几个嵌套元组的函数,我不想手动转换每个。例如,((Int, Int), (Int, Int)) => Int(Int, Int, Int, Int) => Int

Here说可以用shapeless 那么新的函数就想


import shapeless._
import ops.tuple.FlatMapper

trait LowPriorityFlatten extends Poly1 {
  implicit def default[T] = at[T](Tuple1(_))
}

object flatten extends LowPriorityFlatten {
  implicit def caseTuple[P <: Product](implicit lfm: Lazy[FlatMapper[P, flatten.type]]) =
    at[P](lfm.value(_))
}

def ft(f: ((Int, Int, Int)) => Int): ((Int, (Int, Int))) => Int = (p: (Int, (Int, Int)))  => {
  val a: (Int, Int, Int) = flatten(p).asInstanceOf[(Int, Int, Int)]
  f(a)
}

上面的代码有两个问题:

  1. 如何定义函数 ft[A, B, C](f: A => C): B 其中 AB 的扁平类型?
  2. flatten(p) 将产品类型 FlatMapper.this.Out 和错过类型,所以我使用 asInstanceOf 在这里转换类型。

那么,如何编写一个函数来展平参数中任何类型的嵌套元组?

以下代码适用于 Scala 3:

scala> type Flat[T <: Tuple] <: Tuple = T match
     |   case EmptyTuple => EmptyTuple
     |   case h *: t => h match
     |     case Tuple => Tuple.Concat[Flat[h], Flat[t]]
     |     case _     => h *: Flat[t]
     |

scala> def flat[T <: Tuple](v: T): Flat[T] = (v match
     |   case e: EmptyTuple => e
     |   case h *: ts => h match
     |     case t: Tuple => flat(t) ++ flat(ts)
     |     case _        => h *: flat(ts)).asInstanceOf[Flat[T]]
def flat[T <: Tuple](v: T): Flat[T]

scala> def ft[A <: Tuple, C](f: Flat[A] => C): A => C = a => f(flat(a))
def ft[A <: Tuple, C](f: Flat[A] => C): A => C

scala> val f0: ((Int, Int, Int)) => Int = x => x._1 + x._2 + x._3


scala> def g0(f: ((Int, (Int, Int))) => Int): Int = f(1,(2,3))

scala> g0(ft(f0))
val res0: Int = 6

编辑: 添加 scala2 的版本:

  import shapeless._
  import ops.tuple.FlatMapper
  import syntax.std.tuple._

  trait LowPriorityFlat extends Poly1 {
    implicit def default[T] = at[T](Tuple1(_))
  }
  object Flat extends LowPriorityFlat {
    implicit def caseTuple[P <: Product](implicit fm: FlatMapper[P, Flat.type]) =
      at[P](_.flatMap(Flat))
  }

  type F[A, B] = FlatMapper.Aux[A, Flat.type, B]
  
  def flatTup[T <: Product](t: T)(implicit lfm: FlatMapper[T, Flat.type]): lfm.Out = 
    FlatMapper[T, Flat.type].apply(t)

  def flatFun[A <: Product, B <: Product, C](f: B => C)
                                            (implicit lfm: F[A, B]): A => C =
      a => f(flatTup(a))
  
  val f0: ((Int, Double, Int, Double)) => Double = { case(i1, d1, i2, d2) => (i1 + i2) / (d1 + d2) }
  def g0(f: (((Int, Double), (Int, Double))) => Double): Double = f((1, 2.0), (3, 4.0))
  val r0 = g0(flatFun(f0))