scala - 拒绝理解

标签 scala configuration

有没有人尝试过将 for 推导式与拒绝配置/命令行库一起使用?使用 mapN 及其 Opts 类来生成配置案例类,如果它有很多成员,那么它会变得非常不可读且脆弱。我想使用 for-compression 来代替,如下所示:

  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)

如果你想知道使用什么环境变量来设置端口,你必须计数——端口是案例类的第五个成员,所以你必须找到在元组中创建的第五个环境变量。当有很多这样的东西时,这不太好。

评论中建议的以下代码确实有所改进:

  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 推导式的目的吗?看起来使用一个会更干净一些,所以我想知道我是否缺少导入或其他东西,或者库是否真的决定让 Opts 上的 flatMap 变得不可能。

最佳答案

So I'm wondering [...] if the library has genuinely decided to make it impossible to flatMap over Opts.

是的,他们故意决定避免 flatMap,因为传递给前面的选项的参数和后面的选项规范之间不应该有因果关系。例如,允许类似

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

会得出一个荒谬的结论,即需要知道用户名才能显示密码的帮助,因为密码的描述取决于在经过验证的用户名参数上。这就是 IO-monad 在交互式对话中的行为方式,但它不适合 Opts

它是有意设计成适用性的,而不是单一的。因此,没有 flatMap,如果他们试图将其强制进入单子(monad)接口(interface),那将是非常奇怪的,这对于这个用例来说是不必要的限制。

所以,而不是

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

对于单子(monad) m 只需使用

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

适用于应用as。

关于scala - 拒绝理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74449548/

相关文章:

java - 为什么我可以在 Scala 中定义通用异常类型?

scala - 根据时间戳列过滤数据框

scala - 懒惰的 val vs. Scala 中递归流的 val

java - 无法使用 Log4j2 更改日志级别

linux - af_packet.ko 应该自动加载吗?

android - 如何在空白 Android 应用程序中使用 MVVMCross

scala - Play Framework 和 sbt : passing credentials to a nexus passowrd protected repo

java - Paths.get - 指定平台?

python - py2exe 和文件系统

linux - 流式传输时重新加载 ffserver.conf