代码之家  ›  专栏  ›  技术社区  ›  Aaron Novstrup

在scala中提升函数值的方法

  •  8
  • Aaron Novstrup  · 技术社区  · 15 年前

    scala库是否支持将给定类型的方法提升为函数值?

    例如,假设我想 String.length . 我可以写

    val f: String => Int = _.length
    

    val f = { s: String => s.length }
    

    然而,这种语法并不总是理想的(尤其是在更大的表达式中)。我想我在找一些能使表达式

    Lift[String](_.length)
    Lift[Option[Int]].lift(_.filter)
    

    我想到了这样的事情:

    class Lift[T] {                                                          
       def apply[R](f: T => R): T => R = f
    
       def lift[A, R](f: (T) => (A) => R): (T, A) => R = 
             f(_)(_) 
       def lift[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
             f(_)(_,_)
       // ... etc. ...
    }
    object Lift {
       def apply[T] = new Lift[T]
    }
    

    问题1: 标准库(或任何库)是否提供类似的内容?

    问题2: 如果没有, 是否可以这样写 Option.filter 可以如上所述提升 (而不是 Lift[Option[Int]].lift[Int => Boolean, Option[Int]](_.filter) )?不提供上的类型参数 lift 方法我得到以下错误:

    error: missing parameter type for expanded function ((x$1) => x$1.filter)
           Lift[Option[Int]].lift(_.filter)
                                  ^
    

    更新 :

    显然,我遇到的问题与超载有关 举起 方法。如果我重命名重载,我 可以 举起 选项过滤器 没有所有额外的类型参数。

    3 回复  |  直到 15 年前
        1
  •  8
  •   Aaron Novstrup    15 年前

    我终于想出了一个我满意的解决方案。此版本支持简单语法和指向API的单一入口点,同时还提供对提升函数形式的控制(即未转换、部分转换或完全转换)。

    示例 :

    我将在下面的示例中使用以下类定义:

    class Foo {
       def m1: Int = 1
       def m2(i: Int): Int = i
       def m3(i: Int, j: Int): Int = i + j
    }
    

    提升的最简单形式是将方法作为部分应用的函数返回,相当于调用 ((_: Foo).method _) :

    scala> lift[Foo](_.m1)                         // NOTE: trailing _ not required
    res0: (Foo) => Int = <function1>
    
    scala> lift[Foo](_.m2 _)                       // NOTE: trailing _ required
    res1: (Foo) => (Int) => Int = <function1>
    
    scala> lift[Foo](_.m3 _)
    res2: (Foo) => (Int, Int) => Int = <function1> // NOTE: the result is partly curried
    

    通过导入一些隐含内容,您可以请求课程表或未课程表:

    scala> {                        
         | import CurriedLiftables._
         | lift[Foo](_.m3 _)        
         | }
    res3: (Foo) => (Int) => (Int) => Int = <function1>
    
    scala> {                          
         | import UncurriedLiftables._
         | lift[Foo](_.m3 _)          
         | }
    res4: (Foo, Int, Int) => Int = <function3>
    

    实施:

    class Lift[T] {
       def apply[R,F](f: T => R)(implicit e: (T => R) Liftable F): F = e.lift(f)
    }
    object lift {
       def apply[T] = new Lift[T]
    }
    
    class Liftable[From, To](val lift: From => To)
    
    class DefaultLiftables {
       implicit def lift[F]: F Liftable F = new Liftable(identity)
    }
    object Liftable extends DefaultLiftables
    
    class UncurriedLiftable1 extends DefaultLiftables {
       implicit def lift1[T, A, R]: (T => A => R) Liftable ((T, A) => R) = 
          new Liftable( f => f(_)(_) )
    }
    class UncurriedLiftable2 extends UncurriedLiftable1 {
       implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable ((T, A1, A2) => R) = 
          new Liftable ( f => f(_)(_,_) )
    }
    // UncurriedLiftable3, UncurriedLiftable4, ...
    object UncurriedLiftables extends UncurriedLiftable2
    
    class CurriedLiftable2 extends DefaultLiftables {
       implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable (T => A1 => A2 => R) =
          new Liftable( f => (x: T) => (a1: A1) => (a2: A2) => f(x)(a1, a2) )
    }
    // CurriedLiftable3, CurriedLiftable4, ...
    object CurriedLiftables extends CurriedLiftable2
    

    我以前的解决方案需要为每个arity使用单独的提升方法:

    import Lift._
    val f1 = lift0[String](_.length)
    val f2 = lift1[Option[Int]](_.filter)
    val f3 = lift2[Either[String, Int]](_.fold)
    

    实施:

    class Lift0[T] {
       def apply[R](f: T => R): T => R = f
    }
    class Lift1[T] {
       def apply[A, R](f: (T) => (A) => R): (T, A) => R = 
          f(_)(_) 
    }
    class Lift2[T] {
       def apply[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
          f(_)(_,_)
    }
    // ... etc. ...
    
    object Lift {
       def lift0[T] = new Lift0[T]
       def lift1[T] = new Lift1[T]
       def lift2[T] = new Lift2[T]
       // ... etc. ...
    }
    
        2
  •  7
  •   Daniel C. Sobral    15 年前

    有什么问题吗

    (_: String).length
    (_: Option[Int]).filter _
    

    ?

        3
  •  4
  •   michid    15 年前

    作为部分应用的方法传入筛选器似乎可以完成以下工作:

    scala> class Lift[T] {                                        
         |    def apply[R](f: T => R): T => R = f
         | }
    defined class Lift
    
    scala> object Lift {
         |    def apply[T] = new Lift[T]
         | }
    defined module Lift
    
    scala> val ls = Lift[String](_.length)
    ls: (String) => Int = <function1>
    
    scala> val los = Lift[Option[Int]](_.filter _)     
    los: (Option[Int]) => ((Int) => Boolean) => Option[Int] = <function1>
    
    推荐文章