Что такое "подъем" в Scala?



иногда, когда я читаю статьи в экосистеме Scala, я читаю термин "подъем" / "поднятый". К сожалению, не объясняется, что именно это означает. Я провел некоторое исследование, и кажется, что лифтинг имеет какое-то отношение к функциональным значениям или что-то в этом роде, но я не смог найти текст, который объясняет, что такое лифтинг на самом деле в дружественной для начинающих форме.



есть дополнительная путаница через лифт рамки которые имеют подниматься внутри его имя, но это не помогает ответить на вопрос.



что такое "подъем" в Scala?

696   4  

4 ответов:

есть несколько обычаев:

PartialFunction

запомнить PartialFunction[A, B] - это функция, определенная для некоторого подмножества домена A (как указано isDefinedAt метод). Вы можете "поднять"PartialFunction[A, B] на Function[A, Option[B]]. То есть, функция, определенная над весь на A но чьи значения типа Option[B]

это делается явным вызовом метода lift на PartialFunction.

scala> val pf: PartialFunction[Int, Boolean] = { case i if i > 0 => i % 2 == 0}
pf: PartialFunction[Int,Boolean] = <function1>

scala> pf.lift
res1: Int => Option[Boolean] = <function1>

scala> res1(-1)
res2: Option[Boolean] = None

scala> res1(1)
res3: Option[Boolean] = Some(false)

методы

вы можете "поднять" вызов метода в функцию. Это называется eta-expansion (спасибо Бену Джеймсу за это). Так например:

scala> def times2(i: Int) = i * 2
times2: (i: Int)Int

мы поднимаем метод в функцию, применяя подчеркивание

scala> val f = times2 _
f: Int => Int = <function1>

scala> f(4)
res0: Int = 8

обратите внимание на фундаментальное различие между методами и функциями. res0 это экземпляр (т. е. стоимостью) из тип (функция)(Int => Int)

функторы

A функтор (как определено scalaz) какой-то "контейнер" (я использую термин очень свободно), F такие, что, если у нас есть F[A] и A => B, то мы можем получить наши руки на F[B] (подумайте, например, F = List и map способ)

мы можем кодировать это свойство следующим образом:

trait Functor[F[_]] { 
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

это изоморфна чтобы иметь возможность "поднять" функцию A => B в область функтора. То есть:

def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]

то есть, если F является функтором, и у нас есть функция A => B, у нас есть функция F[A] => F[B]. Вы можете попробовать и реализовать lift метод - это довольно нетривиальная.

Монада Трансформаторы

как hcoopz говорит ниже (и я только что понял, что это спасло бы меня от написания тонны ненужного кода), термин " лифт" также имеет значение в пределах Монада Трансформаторы. Напомним, что трансформаторы монады-это способ "укладывания" монад друг на друга (монады не составляют).

так, например, предположим, у вас есть функция, которая возвращает IO[Stream[A]]. Это может быть преобразовано в трансформатор монады StreamT[IO, A]. Теперь вы можете "поднять" какое-то другое значение IO[B] возможно, к тому же это StreamT. Можно написать так:

StreamT.fromStream(iob map (b => Stream(b)))

или это:

iob.liftM[StreamT]

напрашивается вопрос: почему я хочу, чтобы преобразовать IO[B] на StreamT[IO, B]?. Ответ будет заключаться в том, чтобы "воспользоваться возможностями композиции". Допустим, у вас есть функция f: (A, B) => C

lazy val f: (A, B) => C = ???
val cs = 
  for {
    a <- as                //as is a StreamT[IO, A]
    b <- bs.liftM[StreamT] //bs was just an IO[B]
  }
  yield f(a, b)

cs.toStream //is a Stream[IO[C]], cs was a StreamT[IO, C]

другое использование подъем то, что я столкнулся в документах (не обязательно связанных с Scala), перегружает функцию из f: A -> B С f: List[A] -> List[B] (или множеств, мультимножеств, ...). Это часто используется для упрощения формализации, потому что тогда не имеет значения, является ли f применяется к отдельному элементу или нескольким элементам.

этот вид перегрузки часто делается декларативно, например,

f: List[A] -> List[B]
f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n))

или

f: Set[A] -> Set[B]
f(xs) = \bigcup_{i = 1}^n f(xs(i))

или императивно, например,

f: List[A] -> List[B]
f(xs) = xs map f

обратите внимание на любую коллекцию, которая расширяет PartialFunction[Int, A] (как указано oxbow_lakes) может быть поднят; таким образом, например

Seq(1,2,3).lift
Int => Option[Int] = <function1>

который превращает частичную функцию в общую функцию, где значения, не определенные в коллекции, отображаются на None,

Seq(1,2,3).lift(2)
Option[Int] = Some(3)

Seq(1,2,3).lift(22)
Option[Int] = None

кроме того,

Seq(1,2,3).lift(2).getOrElse(-1)
Int = 3

Seq(1,2,3).lift(22).getOrElse(-1)
Int = -1

Это показывает аккуратный подход, чтобы избежать index out of bounds исключения.

есть еще unlifting, что является обратным процессом к подъему.

если подъем определяется как

включение частичной функции PartialFunction[A, B] в общей функция A => Option[B]

тогда unlifting-это

поворот общей функции A => Option[B] в частичную функцию PartialFunction[A, B]

стандартная библиотека Scala определяет Function.unlift как

def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R]

например, библиотека play-json предоставляет unlift помочь со строительством сериализаторы JSON:

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class Location(lat: Double, long: Double)

implicit val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))

Comments

    Ничего не найдено.