Как работать с ADT (запечатанная черта) с помощью конфигурации ZIO - PullRequest
2 голосов
/ 09 января 2020

Как добавить Описание конфигурации вручную для Алгебраи c Тип данных с ZIO Conf .

В примерах Я нашел пример того, как обращаться с ADTs с Magnolia .

Возможно ли это и при добавлении вручную описания конфигурации?

Вот пример:

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)

С Магнолия :

val danceConfig = description[Dance]

Вручную:

val danceConfig = ???

1 Ответ

2 голосов
/ 11 января 2020

Это многословно, как и следовало ожидать. Но есть разные способы сделать это, и это вопрос предпочтения.

Мы постарались быть более многословными, чем требовалось в обоих этих вариантах, для лучшего понимания

Вариант 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, если вы заинтересованы. Большое спасибо за этот вопрос и надеюсь, что ответ будет полезным.

...