Подъемные методы для работы со значениями в Scala - PullRequest
8 голосов
/ 12 ноября 2010

Предоставляет ли библиотека 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)
                              ^

Обновление :

Видимо, проблема, с которой я работаюимеет отношение к перегруженному методу lift.Если я переименую перегрузки, я могу поднять Option.filter без всех дополнительных параметров типа.

Ответы [ 3 ]

8 голосов
/ 13 ноября 2010

Я наконец-то нашел решение, которым я доволен. Эта версия поддерживает простой синтаксис и единую точку входа в 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

Мое предыдущее решение требовало отдельного метода подъема для каждой арности:

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. ...
}
7 голосов
/ 12 ноября 2010

В чем проблема с

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

?

4 голосов
/ 12 ноября 2010

Передача фильтра как частично примененного метода, кажется, делает свою работу:

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...