Scala에서 Applicative Functor를 사용해야하는시기와 이유
나는 알고 Monad
다음과 같이 스칼라으로 표현 될 수있다 :
trait Monad[F[_]] {
def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}
나는 그것이 왜 유용한 지 봅니다. 예를 들어 다음과 같은 두 가지 함수가 있습니다.
getUserById(userId: Int): Option[User] = ...
getPhone(user: User): Option[Phone] = ...
모나드 getPhoneByUserId(userId: Int)
이므로 함수를 쉽게 작성할 수 있습니다 Option
.
def getPhoneByUserId(userId: Int): Option[Phone] =
getUserById(userId).flatMap(user => getPhone(user))
...
이제 Applicative Functor
Scala에서 볼 수 있습니다 .
trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
모나드 대신 언제 사용해야하는지 궁금합니다 . Option과 List는 모두 Applicatives
. apply
Option 및 List와 함께 사용 하는 간단한 예제를 제공 하고 대신 사용해야 하는 이유를 설명해 주 시겠습니까? flatMap
그렇다면 모나드가있을 때 응용 펑터를 사용하는 이유는 무엇입니까? 우선, 우리가 작업하고자하는 추상화에 대해 모나드 인스턴스를 제공하는 것은 불가능합니다
Validation
. 이것이 완벽한 예입니다.둘째, 작업을 완료하기 위해 가장 강력한 추상화를 사용하는 것은 견고한 개발 관행입니다. 원칙적으로 이것은 다른 방법으로는 불가능한 최적화를 허용 할 수 있지만 더 중요한 것은 우리가 작성한 코드를 더 재사용 할 수있게 만든다는 것입니다.
첫 번째 단락에서 약간 확장하려면 : 때때로 모나 딕 코드와 응용 코드 중에서 선택할 수 없습니다. 모델 유효성 검사에 Scalaz (모나드 인스턴스가 없거나 가질 수 없음) 를 사용하려는 이유에 대한 토론은 나머지 답변 을 참조하십시오 Validation
.
최적화 지점에 대해 : Scala 또는 Scalaz에서 일반적으로 관련이있을 때까지는 다소 시간이 걸릴 수 있지만 예를 들어 Haskell의 문서를Data.Binary
참조하십시오 .
적용 가능한 스타일은
binary
읽기를 함께 그룹화하여 코드를 최적화하려고 시도 하므로 코드가 더 빨라질 수 있습니다 .
적용 가능한 코드를 작성하면 계산 간의 종속성에 대한 불필요한 주장을 피할 수 있습니다. 유사한 모나 딕 코드가 사용자에게 위임 할 것이라고 주장합니다. 충분히 똑똑한 라이브러리 나 컴파일러 는 원칙적으로이 사실을 활용할 수 있습니다.
이 아이디어를 좀 더 구체적으로 만들려면 다음 모나 딕 코드를 고려하십시오.
case class Foo(s: Symbol, n: Int)
val maybeFoo = for {
s <- maybeComputeS(whatever)
n <- maybeComputeN(whatever)
} yield Foo(s, n)
for
더 많거나 적은 다음과 같은 뭔가를 -comprehension의 desugars :
val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s, n))
)
우리는 이것이 maybeComputeN(whatever)
의존하지 않는다는 것을 알고 있지만 s
(이것들이 씬 뒤에서 일부 변경 가능한 상태를 변경하지 않는 잘 작동하는 메서드라고 가정) 컴파일러는 그렇지 않습니다 . s
컴퓨팅을 시작하기 전에 알아야 할 관점에서 n
.
적용 가능한 버전 (Scalaz 사용)은 다음과 같습니다.
val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))
여기서 우리는 두 계산간에 종속성이 없음을 명시 적으로 설명합니다.
(예,이 |@|
구문은 매우 끔찍 합니다. 토론 및 대안에 대해서는 이 블로그 게시물 을 참조하십시오 .)
그래도 마지막 요점이 가장 중요합니다. 문제를 해결할 가장 덜 강력한 도구를 선택하는 것은 매우 강력한 원칙입니다. getPhoneByUserId
예를 들어 방법 에서 모나 딕 구성이 필요한 경우도 있지만 그렇지 않은 경우가 많습니다.
Haskell과 Scala 모두 현재 응용 펑터로 작업하는 것보다 모나드 작업을 훨씬 더 편리하게 (구문 적으로 등) 만드는 것은 부끄러운 일이지만 이것은 대부분 역사적 사고의 문제이며 관용구 대괄호 와 같은 개발 은 오른쪽 단계입니다. 방향.
Functor는 계산을 범주로 끌어 올리기위한 것입니다.
trait Functor[C[_]] {
def map[A, B](f : A => B): C[A] => C[B]
}
그리고 그것은 하나의 변수의 함수에 대해 완벽하게 작동합니다.
val f = (x : Int) => x + 1
그러나 2 이상의 함수에 대해 범주로 들어간 후 다음과 같은 서명이 있습니다.
val g = (x: Int) => (y: Int) => x + y
Option(5) map g // Option[Int => Int]
And it is the signature of a applicative functor. And to apply the following value to a function g
— an aplicative functor is needed.
trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
And finally:
(Applicative[Option] apply (Functor[Option] map g)(Option(5)))(Option(10))
Applicative functor is a functor for applying a special value (value in category) to a lifted function.
'program story' 카테고리의 다른 글
example.com은 도메인 예에서 괜찮습니다. IP 주소 예는 무엇입니까? (0) | 2020.11.18 |
---|---|
gdb에서 새 스레드 / 스레드 종료 메시지를 비활성화하려면 어떻게해야합니까? (0) | 2020.11.18 |
org.apache.tomcat.util.bcel.classfile.ClassFormatException : 상수 풀의 잘못된 바이트 태그 : 15 (0) | 2020.11.18 |
아나콘다에서 스파이더를 업데이트하는 방법 (0) | 2020.11.18 |
클래스 메서드와 함께 super 사용 (0) | 2020.11.18 |