Ответ 1
Существует несколько способов использования:
частично определённая функция
Помните, что 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)
Методы
Вы можете "поднять" вызов метода в функцию. Это называется эта-расширением (благодаря Бен-Джеймсу для этого). Так, например:
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)
функторы
Функтор (как определено 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
- это довольно тривиально.
Трансформаторы Monad
Как говорит hcoopz ниже (и я только что понял, что это спасло бы меня от написания тонны ненужного кода), термин "лифт" также имеет смысл в Monad Transformers. Напомним, что монадные трансформаторы - это способ "укладки" монад друг над другом (монады не сочиняют).
Так, например, предположим, что у вас есть функция, которая возвращает IO[Stream[A]]
. Это можно преобразовать в трансформатор монады StreamT[IO, A]
. Теперь вы можете захотеть "поднять" другое значение 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]