복붙노트

[SCALA] 스칼라의 미래 [옵션]를위한 지능형

SCALA

스칼라의 미래 [옵션]를위한 지능형

나는 선물을 돌려 두 가지 기능을 가지고있다. 나는에 대한 수율 이해를 사용하여 다른에 첫 번째 함수에서 수정 된 결과를 공급하기 위해 노력하고있어.

이 방법은 작동합니다 :

  val schoolFuture = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- schoolStore.getSchool(sid.get) if sid.isDefined
  } yield s

그러나 내가 가진 행복하지 해요 거기에, 내가 대신지도를 사용할 수 있어야 것으로 보인다 "만일".

그러나 나는지도 할 때 :

  val schoolFuture: Future[Option[School]] = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- sid.map(schoolStore.getSchool(_))
  } yield s

나는 컴파일 오류가 발생합니다 :

[error]  found   : Option[scala.concurrent.Future[Option[School]]]
[error]  required: scala.concurrent.Future[Option[School]]
[error]         s <- sid.map(schoolStore.getSchool(_))

나는 몇 가지 변화와 주변 연주했지만, 작품이 매력적인 아무것도 발견하지 않았습니다. 사람이 더 좋은 이해를 제안 및 / 또는 제 2 예 뭐가 잘못 됐는지 설명 할 수 있습니까?

다음은 스칼라 2.10로 최소하지만 완전한 실행 가능한 예제입니다 :

import concurrent.{Future, Promise}

case class User(userId: Int)
case class UserDetails(userId: Int, schoolId: Option[Int])
case class School(schoolId: Int, name: String)

trait Error

class UserStore {
  def getUserDetails(userId: Int): Future[Either[Error, UserDetails]] = Promise.successful(Right(UserDetails(1, Some(1)))).future
}

class SchoolStore {
  def getSchool(schoolId: Int): Future[Option[School]] = Promise.successful(Option(School(1, "Big School"))).future
}

object Demo {
  import concurrent.ExecutionContext.Implicits.global

  val userStore = new UserStore
  val schoolStore = new SchoolStore

  val user = User(1)

  val schoolFuture: Future[Option[School]] = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- sid.map(schoolStore.getSchool(_))
  } yield s
}

해결법

  1. ==============================

    1.약속 [옵션 [A] 힘의 도움에 대한 비슷한 질문이 대답. 그냥 약속을 위해 미래를 대체합니다.

    약속 [옵션 [A] 힘의 도움에 대한 비슷한 질문이 대답. 그냥 약속을 위해 미래를 대체합니다.

    나는 당신의 질문에서 getUserDetails 및 getSchool에 대한 다음과 같은 유형을 추론하고 있습니다 :

    getUserDetails: UserID => Future[Either[??, UserDetails]]
    getSchool: SchoolID => Future[Option[School]]
    

    대신 옵션으로 바뀌는 상기 어느 하나로부터 오류 값을 무시하므로, 효율적으로 A 형의 두 값이 => 미래 [옵션 [B].

    당신은 미래를위한 모나드 인스턴스를했으면 (scalaz에 하나있을 수 있습니다, 또는 당신은 내가 링크 된 대답으로 자신을 쓸 수 있습니다) 다음과 같이 보일 것이다 문제에 대한 OptionT 변압기를 적용 :

    for {
      ud  <- optionT(getUserDetails(user.userID) map (_.right.toOption))
      sid <- optionT(Future.successful(ud.schoolID))
      s   <- optionT(getSchool(sid))
    } yield s
    

    호환되는 형식을 유지하기 위해 다음 사항을 참고 ud.schoolID는 (이미 완료) 미래에 싸여있다.

    이것에 대한-이해의 결과는 타입 OptionT [미래, SchoolID]을 것이다. 당신은 유형 미래 [옵션 [SchoolID]]의 값을 갖는 변압기의 실행 방법을 추출 할 수 있습니다.

  2. ==============================

    2.(편집은 정확한 답을 줄 수 있습니다!)

    (편집은 정확한 답을 줄 수 있습니다!)

    열쇠는 여기에 미래와 옵션 올바른 flatMap 서명이 없기 때문에 아니 작성 내부를 할 것입니다. 말씀 드리지만, desugars에 대한 같은 :

    for ( x0 <- c0; w1 = d1; x1 <- c1 if p1; ... ; xN <- cN) yield f
    c0.flatMap{ x0 => 
      val w1 = d1
      c1.filter(x1 => p1).flatMap{ x1 =>
        ... cN.map(xN => f) ... 
      }
    }
    

    (있는 경우 문을 체인으로 필터를 던졌습니다 곳 -과는 체인의 다음 부분 전에 변수를 설정 문을 동일 - I've은 단지 하나의 예를 제공). 당신은 단지 flatMap 다른 선물, 모든 문 C0, C1은 ... 마지막을 제외하고 더 나은 미래를 생산했다 수 있기 때문에.

    - 이제 getUserDetails 및 getSchool 모두 생산 선물,하지만 시드 그래서 우리는의 <오른쪽에 넣을 수없는 옵션입니다. 불행하게도,이 작업을 수행 할 깨끗한 아웃 - 오브 - 박스 방법이 없다. O가 옵션 인 경우, 우리는 할 수

    o.map(Future.successful).getOrElse(Future.failed(new Exception))
    

    이미 완성 된 미래에 옵션을 켭니다. 그래서

    for {
      ud <- userStore.getUserDetails(user.userId)  // RHS is a Future[Either[...]]
      sid = ud.right.toOption.flatMap(_.schoolId)  // RHS is an Option[Int]
      fid <- sid.map(Future.successful).getOrElse(Future.failed(new Exception))  // RHS is Future[Int]
      s <- schoolStore.getSchool(fid)
    } yield s
    

    트릭을 할 것입니다. 그게 당신이있어보다 더 나은가요? 못 미더운. 하지만 경우

    implicit class OptionIsFuture[A](val option: Option[A]) extends AnyVal {
      def future = option.map(Future.successful).getOrElse(Future.failed(new Exception))
    }
    

    다음 갑자기을 위해-이해 합리적인 다시 보이는 :

    for {
      ud <- userStore.getUserDetails(user.userId)
      sid <- ud.right.toOption.flatMap(_.schoolId).future
      s <- schoolStore.getSchool(sid)
    } yield s
    

    이것은이 코드를 작성하는 가장 좋은 방법이 있나요? 아마 아닙니다; 그것은 당신이 그 시점에서 다른 무엇을해야할지 몰라 간단하기 때문에 예외에 없음을 변환하지 않습니다에 의존한다. 이 때문에 미래의 디자인 결정의 주위에 일하기 어렵다; 나는 (필터를 호출) 원래 코드가 할 수있는 방법의 좋은으로 적어도 것을 건의 할 것입니다.

  3. ==============================

    3.어떤 행동을하면이 옵션 [학교] 아무도 없다는 경우에 발생하는 하시겠습니까? 미래처럼 당신은 실패 할 것인가? 예외의 종류와? 좋아 당신은 결코 완료하는 데겠습니까? (나쁜 생각처럼 그 소리).

    어떤 행동을하면이 옵션 [학교] 아무도 없다는 경우에 발생하는 하시겠습니까? 미래처럼 당신은 실패 할 것인가? 예외의 종류와? 좋아 당신은 결코 완료하는 데겠습니까? (나쁜 생각처럼 그 소리).

    여하튼, 필터 메소드 호출에 대한 표현 desugars의 경우 절. 미래에 # 계약은 따라서 filteris :

    하지만 기다려:

    scala> None.get
    java.util.NoSuchElementException: None.get
    

    당신이 볼 수 있듯이, None.get 똑같은 일을 반환합니다.

    따라서, sid.isDefined 일을해야하는 경우 치우는, 이것은 합리적인 결과를 반환해야합니다 :

      val schoolFuture = for {
        ud <- userStore.getUserDetails(user.userId)
        sid = ud.right.toOption.flatMap(_.schoolId)
        s <- schoolStore.getSchool(sid.get)
      } yield s
    

    schoolFuture의 결과가 scala.util.Failure [예외 : NoSuchElementException]의 인스턴스가 될 수 있음을 유의하십시오. 그러나 당신은 당신이 원하는 어떤 다른 행동을 설명하지 않았다.

  4. ==============================

    4.우리는 하나의 모나드와 같은 역할을하지 미래 [옵션 [T]]에 작은 래퍼를했습니다 (모나드 법률의 아무도도 확인 아무도하지만,지도, flatMap, foreach는, 필터 등이있다) - MaybeLater. 그것은 비동기 옵션보다 훨씬 더 작동합니다.

    우리는 하나의 모나드와 같은 역할을하지 미래 [옵션 [T]]에 작은 래퍼를했습니다 (모나드 법률의 아무도도 확인 아무도하지만,지도, flatMap, foreach는, 필터 등이있다) - MaybeLater. 그것은 비동기 옵션보다 훨씬 더 작동합니다.

    이 냄새 나는 코드를 많이 있습니다,하지만 어쩌면 그것은 예를 들어 적어도 도움이 될 것입니다. 의 : 열려 많은 질문이 있습니다 (여기 예에 대한이.)

  5. ==============================

    5.그것은 A-일반 형식 변환 할 https://github.com/qifun/stateless-future 또는 https://github.com/scala/async을 사용하기 쉽습니다.

    그것은 A-일반 형식 변환 할 https://github.com/qifun/stateless-future 또는 https://github.com/scala/async을 사용하기 쉽습니다.

  6. from https://stackoverflow.com/questions/14385633/futureoption-in-scala-for-comprehensions by cc-by-sa and MIT license