如何使用ZIO config处理ADT(密封特质)

3
我该如何使用ZIO Conf手动添加代数数据类型配置描述?在示例中,我找到了一个关于如何使用Magnolia处理ADTs的示例。当手动添加配置描述时,这也是可能的吗?以下是一个示例:
sealed trait Dance
final case class A(any: Person)   extends Dance
final case class B(body: Height)  extends Dance

final case class Person(name: String, age: Option[Int])
final case class Height(height: Long)

使用 Magnolia
val danceConfig = description[Dance]

手动:
val danceConfig = ???
1个回答

2

正如你所预期的那样,它很啰嗦。但有不同的方法来做到这一点,这是一个偏好问题。

我们在这两个选项中都尝试更加冗长,以便更好地理解。

选项1:

  val personConfig =
    (string("name") |@| int("age").optional)(Person.apply, Person.unapply)

  val heightConfig =
    long("height").xmap(Height)(_.height)

  val aConfig = nested("any")(personConfig).xmap(A)(_.any)
  val bConfig = nested("body")(heightConfig).xmap(B)(_.body)
  val cConfig = boolean("can").xmap(C)(_.can)
  val dConfig = string("dance").xmap(D)(_.dance)

  val danceConfig =
    aConfig
      .orElseEither(bConfig)
      .orElseEither(cConfig)
      .orElseEither(dConfig)
      .xmap({
        case Right(value) => value: Dance
        case Left(value) =>
          value match {
            case Right(value) => value: Dance
            case Left(value) =>
              value match {
                case Right(value) => value: Dance
                case Left(value)  => value: Dance
              }
          }
      })({
            case d @ D(_) => Right(d)
            case c @ C(_) => Left(Right(c))
            case b @ B(_) => Left(Left(Right(b)))
            case a @ A(_) => Left(Left(Left(a)))
          }
      )

写入时有些复杂,但全部基于类型驱动。

选项2

   val personConfig =
    (string("name") |@| int("age").optional)(Person.apply, Person.unapply)

  val heightConfig =
    long("height").xmap(Height)(_.height)

  val aConfig = nested("any")(personConfig).xmap(A)(_.any)
  val bConfig = nested("body")(heightConfig).xmap(B)(_.body)
  val cConfig = boolean("can").xmap(C)(_.can)
  val dConfig = string("dance").xmap(D)(_.dance)

  val aConfigAsDance =
    aConfig.xmapEither(a => Right(a: Dance))({
      case a: A => Right(a)
      case _    => Left("unable to write back") 
    })

  val bConfigAsDance =
    bConfig.xmapEither(a => Right(a: Dance))({
      case a: B => Right(a)
      case _    => Left("unsable to write back")
    })

  val cConfigAsDance =
    cConfig.xmapEither(a => Right(a: Dance))({
      case a: C => Right(a)
      case _    => Left("unsable to write back")
    })

  val dConigAsDance =
    dConfig.xmapEither(a => Right(a: Dance))({
      case a: D => Right(a)
      case _    => Left("unsable to write back")
    })

  val danceConfig =
    aConfigAsDance.orElse(bConfigAsDance).orElse(cConfigAsDance).orElse(dConigAsDance)



你会注意到,在写入部分(xmapEither的第二个参数)中,我们确保它是正确的类型。例如:在 aConfigAsDance 中,假设它只能是A并进行 asInstanceOf 是不安全的。

通过使用 xmapEither ,我们能够编写安全且纯净的代码,并且我们已经遵循了这种方式。

未来,ZIO-Config 将推出一些帮助函数来处理 Either。这是因为 ZIO-Config 的哲学是尽可能为用户提供尽可能少的神奇接口,而您仍然可以使用 zio-config-magnolia 将它们缩短为一行,即

val danceConfig = description[Dance]

如果您感兴趣,很高兴在zio-config中看到这个例子。非常感谢这个问题,希望答案有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接