Что такое "подъем" в Scala?
иногда, когда я читаю статьи в экосистеме Scala, я читаю термин "подъем" / "поднятый". К сожалению, не объясняется, что именно это означает. Я провел некоторое исследование, и кажется, что лифтинг имеет какое-то отношение к функциональным значениям или что-то в этом роде, но я не смог найти текст, который объясняет, что такое лифтинг на самом деле в дружественной для начинающих форме.
есть дополнительная путаница через лифт рамки которые имеют подниматься внутри его имя, но это не помогает ответить на вопрос.
что такое "подъем" в Scala?
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) => Clazy 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