답변:
몇 가지 사용법이 있습니다.
a PartialFunction[A, B]
는 도메인의 일부 하위 집합에 대해 정의 된 함수입니다 A
( isDefinedAt
메소드에서 지정한대로 ). a를 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
을 functor의 영역으로 "리프팅"할 수있는 동형 입니다. 그건:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
즉, F
functor 인 경우 함수 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]
MonadTrans
T
M
Monad
N
T.liftM
N[A]
M[N, A]
liftM
하고 싶지만 제대로 관리하는 방법을 이해하지 못했습니다. 여러분, 당신은 바위입니다!
f
, 인스턴스가 아닌 수 res0
?
논문 (스칼라 관련 문서는 아니지만)에서 들어 본 리프팅의 또 다른 사용법은 f: A -> B
with 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
이것은 범위를 벗어난 인덱스 예외 를 피하는 깔끔한 접근 방식을 보여줍니다 .
도있다 unlifting 리프팅의 역 과정이다.
리프팅이 다음과 같이 정의 된 경우
부분 함수
PartialFunction[A, B]
를 전체 함수로 바꾸기A => Option[B]
그런 다음 해제
전체 함수
A => Option[B]
를 부분 함수로 바꾸기PartialFunction[A, B]
스칼라 표준 라이브러리를 정의 Function.unlift
로
def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R]
예를 들어, play-json 라이브러리는 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))