program story

Scala에서 Applicative Functor를 사용해야하는시기와 이유

inputbox 2020. 11. 18. 09:02
반응형

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 FunctorScala에서 볼 수 있습니다 .

trait Applicative[F[_]] {
  def apply[A, B](f: F[A => B]): F[A] => F[B]
}

모나드 대신 언제 사용해야하는지 궁금합니다 . Option과 List는 모두 Applicatives. applyOption 및 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.

참고URL : https://stackoverflow.com/questions/19880207/when-and-why-should-one-use-applicative-functors-in-scala

반응형