scala 拒绝理解

omhiaaxx  于 2022-11-23  发布在  Scala
关注(0)|答案(1)|浏览(149)

有没有人尝试过在decline config/command-line库中使用for-comprehension?使用mapN和他们的Opts类来生成一个config case类,如果它有很多成员的话,会变得非常难读和脆弱。我想使用一个for-comprehension来代替,如下所示:

val databaseConfig: Opts[DatabaseConfig] = {
    for {
      username <- Opts.envWithDefault[String]("POSTGRES_USER", "Postgres username", "postgres")
      password <- Opts.envWithDefault[String]("POSTGRES_PASSWORD", "Postgres password", "postgres")
      hostname <- Opts.envWithDefault[String]("POSTGRES_HOSTNAME", "Postgres hostname", "localhost")
      database <- Opts.envWithDefault[String]("POSTGRES_DATABASE", "Postgres database", "thebean")
      port <- Opts.envWithDefault[Int]("POSTGRES_PORT", "Postgres port", 5432)
      threadPoolSize <- Opts.envWithDefault[Int]("POSTGRES_THREAD_POOL_SIZE", "Postgres thread pool size", 4)
    } yield DatabaseConfig(username, password, hostname, database, port, threadPoolSize)

但这似乎是不可能的,因为Opts没有定义flatMap,而且我也没有看到一个好的实现它的方法(这并不是说没有)。有什么建议吗?我错过了这个神奇的导入吗?
编辑:
有问题的代码如下所示(真正有问题的代码有更多的成员,但这给出了想法):

(
    Opts.envWithDefault[String]("POSTGRES_USER", "Postgres username", "postgres"),
    Opts.envWithDefault[String]("POSTGRES_PASSWORD", "Postgres password", "postgres"),
    Opts.envWithDefault[String]("POSTGRES_HOSTNAME", "Postgres hostname", "localhost"),
    Opts.envWithDefault[String]("POSTGRES_DATABASE", "Postgres database", "thebean"),
    Opts.envWithDefault[Int]("POSTGRES_PORT", "Postgres port", 5432),
    Opts.envWithDefault[Int]("POSTGRES_THREAD_POOL_SIZE", "Postgres thread pool size", 4)
  ).mapN(DatabaseConfig.apply)

如果你想知道什么环境变量被用来设置,比如说,端口,你必须 count --端口是case类的第五个成员,所以你必须找到元组中创建的第五个环境变量,当有很多这样的环境变量时,这就不太好了。
下面的代码(在一个注解中建议使用)确实可以改进一些东西:

val username = Opts.envWithDefault[String]("POSTGRES_USER", "Postgres username", "postgres") 
  val password = Opts.envWithDefault[String]("POSTGRES_PASSWORD", "Postgres password", "postgres") 
  val hostname = Opts.envWithDefault[String]("POSTGRES_HOSTNAME", "Postgres hostname", "localhost") 
  val database = Opts.envWithDefault[String]("POSTGRES_DATABASE", "Postgres database", "thebean") 
  val port = Opts.envWithDefault[Int]("POSTGRES_PORT", "Postgres port", 5432)
  val threadPoolSize = Opts.envWithDefault[Int]("POSTGRES_THREAD_POOL_SIZE", "Postgres thread pool size", 4)

  (username, password, hostname, database, port, threadPoolSize).mapN(DatabaseConfig.apply)

但是这不正是for-comprehension的目的吗?看起来使用一个for-comprehension会更干净一些,所以我想知道我是否遗漏了一个import或其他什么,或者库是否真的决定不可能在Opts上进行flatMap。

djmepvbi

djmepvbi1#

所以我想知道[...]如果图书馆真的决定让它不可能flatMap超过选择。
是的,他们有意地决定避免flatMap,因为传递给前面的选项的参数和后面的选项规范之间不应该有因果关系。

for
  username <- Opts.envWithDefault[String]("X", "Postgres username", "W") 
  password <- Opts.envWithDefault[String]("Y", s"Password of ${username}", "Z") 
yield SomeConfig(username, password)

将导致荒谬的结论,即在显示password的帮助之前需要知道username,因为password的描述依赖于经过验证的username参数。这是IO-单子在交互式对话中的行为方式,但它不适用于Opts
它被有意地做成应用型的,而不是单值的。因此,不存在flatMap,如果他们试图把它强制到单值接口中,那将是非常奇怪的,这对这个用例来说是不必要的限制。
所以,与其

for 
  x <- m1
  y <- m2
  z <- m3
yield Foo(x, y, z)

对于一元m,只需使用

val x = a1
val y = a2
val z = a3
(a1, a2, a3).mapN(Foo.apply)

适用于a s。

相关问题