如何使用Shapeless创建一个抽象的arity函数

ave*_*net 7 scala shapeless

让我们考虑一个具体的例子.我有很多函数采用可变数量的参数,并返回一个Seq[T].说:

def nonNeg(start: Int, count: Int): Seq[Int] = 
    Iterator.from(start).take(count).toSeq
Run Code Online (Sandbox Code Playgroud)

对于这些函数中的每一个,我需要创建该函数的"Java版本",返回一个java.util.List[T].我可以使用以下命令创建上述函数的"Java版本":

def javaNonNeg(start: Int, count: Int): java.util.List[Int] =
    nonNeg(start, count).asJava
Run Code Online (Sandbox Code Playgroud)

这有点冗长,因为参数列表重复两次.相反,我想创建一个更高级别的函数,它将参数作为nonNeg(任何数量和类型的参数,返回a Seq[T])形式的函数,并返回一个接受相同参数的函数,但返回一个java.util.List[T].假设函数被调用makeJava,那么我就可以写:

def javaNonNeg = makeJava(nonNeg)
Run Code Online (Sandbox Code Playgroud)

可以makeJava使用Shapeless的能力来编写抽象的arity吗?如果可以,怎么做,不是,为什么以及如何做到这一点?

Tra*_*own 8

可以使用Shapeless来避免样板 - 你只需要将原始方法转换为FunctionN使用普通的旧eta扩展,然后转换为采用单个HList参数的函数,然后返回到FunctionN具有新结果类型的函数:

import java.util.{ List => JList }
import shapeless._, ops.function._
import scala.collection.JavaConverters._

def makeJava[F, A, L, S, R](f: F)(implicit
  ftp: FnToProduct.Aux[F, L => S],
  ev: S <:< Seq[R],
  ffp: FnFromProduct[L => JList[R]]
) = ffp(l => ev(ftp(f)(l)).asJava)
Run Code Online (Sandbox Code Playgroud)

然后:

scala> def nonNeg(start: Int, count: Int): Seq[Int] = 
     |     Iterator.from(start).take(count).toSeq
nonNeg: (start: Int, count: Int)Seq[Int]

scala> val javaNonNeg = makeJava(nonNeg _)
javaNonNeg: (Int, Int) => java.util.List[Int] = <function2>

scala> javaNonNeg(1, 4)
res0: java.util.List[Int] = [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

javaNonNeg是一个Function2,所以从Java你可以使用javaNonNeg.apply(1, 4).