Слик для понимания: как обращаться с перечислителем, который может быть None или Some-thing? - PullRequest
1 голос
/ 28 мая 2019

У меня есть сценарий использования, возможно, не специфический для Слика, а для понимания. Шаблон высокого уровня подобен этому, но это приводит к ошибке компилятора в enum3:

(for {
   enum1 <- // ...
   enum2 <- // ...
   enum3 <- optionArg.fold(empty result).map { ... }
 } yield ())

Затем я выполняю следующее, которое компилируется, но имеет дублирование кода, а именно enum1 и enum2:

optionArg match { 
  case (Some(arg)) => {
     (for {
        enum1 <- // ...
        enum2 <- // ...
        enum3 <- // do something with arg          
      } yield ())
    }
    case None => {
     (for {
        enum1 <- // ...
        enum2 <- // ...
      } yield ())
    }
  }        
}  

Конкретно у меня есть, что этот повторяющийся вариант компилируется:

/**
 * Returns the inserted `oauth2Info` instance including the params. We first
 * look up the `LoginInfo` by the relevant search criteria, fetching its `userId`
 * which is then used to persist a `OAuth2Info` and multiple `OAuth2InfoParam`.
 *
 * @param extLoginInfo The login info for which the auth info should be added.
 * @param extOAuth2Info The TOTP info to add containing the params.
 * @return the inserted `oauth2Info` instance including the params.
 */
def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
    val insertion = extOAuth2Info.params match {
      case Some(params) => {
        (for {
          userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
          _ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
          _ <- DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
        } yield ())
      }
      case None => {
        (for {
          userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
          _ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
        } yield ())
      }
    }
    db.run(insertion.transactionally).map(_ => extOAuth2Info)
}

и этого краткого желаемого варианта нет:

def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
    val insertion = (for {
      userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
      _ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
      _ <- extOAuth2Info.params.fold(DBIOAction.seq()) { params =>
        DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
      }
    } yield ()).transactionally
    db.run(insertion).map(_ => extOAuth2Info)
}

Дает ошибку компиляции:

[play-silhouette-seed] $ compile
[info] Formatting 1 Scala source ProjectRef(uri("file:/home/skywalker/code/play-silhouette-seed/"), "root")(compile) ...
[info] Compiling 1 Scala source to /home/skywalker/code/play-silhouette-seed/target/scala-2.12/classes ...
[error] /home/skywalker/code/play-silhouette-seed/app/models/daos/OAuth2InfoDaoImpl.scala:58:28: type mismatch;
[error]  found   : slick.dbio.DBIOAction[scala.collection.immutable.Iterable[Int],slick.dbio.NoStream,slick.dbio.Effect.Write]
[error]  required: slick.dbio.DBIOAction[Unit,slick.dbio.NoStream,slick.dbio.Effect]
[error]         DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
[error]                            ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed May 28, 2019 10:42:00 AM

1 Ответ

1 голос
/ 28 мая 2019

Это проблема с fold, когда компилятор не может ввести правильные универсальные типы для выражения.Он неправильно расширяет тип, возвращаемый из первого выражения, до типа второго.Вам нужно немного помочь компилятору.

Это также происходит в таких ситуациях

 Some(123).fold(None)(_ => Some("123"))
 // type mismatch; found: Some[String] required: None.type

Вы можете изменить fold на map().getOrElse() (тогда «хороший тип» появляется первыми получите вывод).

Или вы можете добавить где-нибудь аннотации типа, например

 private val noAction: slick.dbio.DBIOAction[Iterable[Int],NoStream,Effect.Write] 
   = DBIOAction.sequence()

 theOption.fold(noAction)(params => .... )

Кроме того, если вы обнаружите, что дублируете код, это можно уменьшить, переместивобщее выражение вне.С функциональным (без побочных эффектов) кодом, таким как Slick, этот вид рефакторинга довольно безопасен.Вы можете просто создать пару val или def с помощью действий Slick, которые необходимо выполнить, и составить их позже.

def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
    val getUserId = LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
    val insertOAuth = for {
        userId <- getUserId 
        _ = (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
    } yield userId


    val insertion = extOAuth2Info.params match {
      case Some(params) => {
        (for {
          userId <- insertOAuth
          _ <- DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
        } yield ())
      }
      case None => 
         insertOAuth
    }
    db.run(insertion.transactionally).map(_ => extOAuth2Info)
}

...