代码之家  ›  专栏  ›  技术社区  ›  raul ferreira

使用shapeless对arity进行抽象

  •  2
  • raul ferreira  · 技术社区  · 8 年前

    使用shapeless,我们可以使用他们文档中描述的配方来抽象算术。

    import shapeless.ops.function.FnToProduct
    import shapeless.{Generic, HList}
    import shapeless._
    import syntax.std.function._
    
    def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F)(implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) =
      f.toProduct(gen.to(p))
    
    val add = (x: Int, y: Int) => x + y
    
    applyProduct(1, 2)(add)
    

    def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F)(implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) =
      f.toProduct(gen.to(p))
    
    val add = (x: Int, y: Int) => x + y
    
    def wrapper[F](f: F) {
      applyProduct(1, 2)(f)
    }
    
    wrapper(add)
    

    编译人认为它不能 find implicit value for parameter fp: shapeless.ops.function.FnToProduct.Aux[(A, A) => A,L => R]

    也就是说,它无法将K元函数转换为具有相同参数类型的K大小HList的函数。

    编辑:

    case class Node[P <: Product, F, L <: HList, R](f: F)(implicit gen:Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) {
      def evaluate(p: P) = {
        f.toProduct(gen.to(p))
      }
    }
    

    编译器不允许我调用:

    val add = (x: Int, y: Int) => x + y
    
    val n = Node(add)
    
    //later
    
    n.evaluate(1,2,3)
    

    我不能用部分应用程序来解决这个问题吗?

    1 回复  |  直到 8 年前
        1
  •  3
  •   dk14    8 年前

    就这么简单:

    def wrapper[R](f: (Int, Int) => R) = {
      applyProduct(1, 2)(f)
    }
    

    您已经定义了 P 类型(as (Int, Int) )顺便 (1,2) 在你的包装里面,所以把它抽象出来没有任何意义 F -你唯一能做的就是 R (结果类型)

    说明:

    applyProduct 在…内 wrapper 什么都不知道 F FnToProduct.Aux[F, ...] scala编译器需要了解更多关于 是,因为“形状” FnToProduct.Aux (A, A) => A (Int, Int) => Int ),而不仅仅是 F ,编译器会从错误消息中如实地告诉您。


    对编辑的响应:

    @ applyProduct(_: Int, _: Int)(add) 
    res17: (Int, Int) => Int = 
    
    @ res17(1,2) 
    res18: Int = 3
    

    为了避免类型归属,可以使用类似 Node case类(使用case类只是为了避免 new 节点 case Scala在不传递所有类型参数的情况下无法进行正确的类型推断。

    不幸的是,你甚至不能在这里方便地使用咖喱(通过制作 f: F

    @ val ff = add _; val f = ff() 
    ff: () => (Int, Int) => Int = 
    f: (Int, Int) => Int = ammonite.$sess.cmd9$$$Lambda$1978/545666041@6e7e60bb
    
    @ f(1,2) 
    res34: Int = 3
    

    让一个函数接受两个(或一系列)参数(reducer),但将其转换为任意算术的函数更有意义,如 def abstract[...](f: (A, A) => A)(p: P): A . 这将是对算术更真实的抽象。

    推荐文章