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
可以解除上述的方式编写它(而不是{{ 1}})?如果不在Lift[Option[Int]].lift[Int => Boolean, Option[Int]](_.filter)
方法上提供类型参数,我会收到以下错误:
error: missing parameter type for expanded function ((x$1) => x$1.filter) Lift[Option[Int]].lift(_.filter) ^
更新:
显然,我遇到的问题与重载的lift
方法有关。如果我重命名重载,我可以解除lift
而没有所有额外的类型参数。
答案 0 :(得分:8)
我终于想出了一个我很满意的解决方案。此版本支持简单的语法和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
通过导入一些含义,可以请求curry或uncurried表单:
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. ...
}
答案 1 :(得分:7)
有什么问题
(_: String).length
(_: Option[Int]).filter _
答案 2 :(得分:4)
传入过滤器作为部分应用的方法似乎可以完成这项工作:
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>