How can I use Shapeless to create a function abstracting over arity

前端 未结 2 1284
终归单人心
终归单人心 2021-02-06 08:40

Let\'s consider a specific example. I have lots of functions that take a variable number of arguments, and return a Seq[T]. Say:

def nonNeg(start: I         


        
相关标签:
2条回答
  • 2021-02-06 09:16

    For 2 and more (in code below to 4) parameters you can use implicit parameters feature, for resolve result type by input parameter type

    sealed trait FuncRes[F] {
      type Param
      type Result
      def func : F => Param => Result
    }
    
    class Func[T, R](fn : T => R) {
      trait FR[F, P] extends FuncRes[F] { type Param = P; type Result = R }
    
      implicit def func2[T1,T2] = new FR[(T1,T2) => T, (T1,T2)] {
        def func = f => p => fn(f.tupled(p))
      }
    
      implicit def func3[T1,T2,T3] = new FR[(T1,T2,T3) => T, (T1,T2,T3)] {
        def func = f => p => fn(f.tupled(p))
      }
    
      implicit def func4[T1,T2,T3,T4] = new FR[(T1,T2,T3,T4) => T, (T1,T2,T3,T4)] {
        def func = f => p => fn(f.tupled(p))
      }
    
      def makeFunc[F](f : F)(implicit ev : FuncRes[F]): ev.Param => ev.Result = 
        ev.func(f)
    }
    

    and after your def javaNonNeg = makeJava(nonNeg) function will look like:

    object asJavaFunc extends Func((_ : Seq[Int]).asJava)  
    import asJavaFunc._
    
    def javaNonNeq = makeFunc(nonNeg _)  
    

    And of course it has some disadvantages, but generally it satisfy your needs.

    0 讨论(0)
  • 2021-02-06 09:29

    It is possible to use Shapeless to avoid the boilerplate—you just need to turn the original method into a FunctionN using plain old eta expansion, then convert to a function taking a single HList argument, and then back to a FunctionN with the new result type:

    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)
    

    And then:

    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]
    

    javaNonNeg is a Function2, so from Java you can use javaNonNeg.apply(1, 4).

    0 讨论(0)
提交回复
热议问题