簡體   English   中英

如何用 ZIO 解釋最終的無標簽 DSL?

[英]How to interpret a final tagless DSL with ZIO?

我有一個最終的無標簽 DSL 來構建簡單的數學表達式:

trait Entity[F[_]] {
  def empty: F[Int]
  def int(value: Int): F[Int]
}

trait Operation[F[_]] {
  def add(a: F[Int], b: F[Int]): F[Int]
}

我想實現一個 ZIO 解釋器。 根據module-pattern guide ,可能的實現如下所示:

type Entity = Has[Entity[UIO]]

object Entity {
  val test: ULayer[Entity] =
    ZLayer.succeed {
      new Entity[UIO] {
        override def empty: UIO[Int] =
          ZIO.succeed(0)

        override def int(value: Int): UIO[Int] =
          ZIO.succeed(value)
      }
    }

  def empty: URIO[Entity, Int] =
    ZIO.accessM(_.get.empty)

  def int(value: Int): URIO[Entity, Int] =
    ZIO.accessM(_.get.int(value))
}

type Operation = Has[Operation[UIO]]

object Operation {
  val test: ULayer[Operation] =
    ZLayer.succeed {
      new Operation[UIO] {
        override def add(a: UIO[Int], b: UIO[Int]): UIO[Int] =
          ZIO.tupled(a, b).map { case (x, y) => x + y }
      }
    }

  def add(a: UIO[Int], b: UIO[Int]): URIO[Operation, Int] =
    ZIO.accessM(_.get.add(a, b))
}

使用此實現構建表達式時,必須像這樣重復調用provideLayer

Operation.subtract(
  Entity.empty.provideLayer(Entity.test),
  Entity.int(10).provideLayer(Entity.test)
).provideLayer(Operation.test)

這看起來更像是一種反模式。 解釋 DSL 的最慣用或最 ZIO 的方式是什么?

從問題中並不清楚您要達到什么目標,但讓我嘗試回答。

  1. ZIO 的R參數與構建 DSL 沒有直接關系。 一旦你構建了你的 DSL, R可以潛在地幫助你以符合人體工程學的方式將它傳遞給你的計算(但可能不是)。

  2. DSL 不是一個精確的術語,但ZIO仍然不太可能幫助您構建它。 DSL 通常基於簡單的可自省數據類型(所謂的初始編碼)或具有大量F的抽象數據類型(最終編碼)。 ZIO數據類型既不是抽象的也不是可自省的。

通過更好地了解 ZIO 回到這個問題,我找到了解決方案。 這是一種變通方法,不符合 ZIO 的精神,不過,我認為它可能值得分享。

我更新了 ZIO 的操作實現:

type Operation = Has[Service[URIO[Entity, *]]]

object Operation {
  val live: ULayer[Operation] =
    ZLayer.succeed {
      new Service[URIO[Entity, *]] {
        override def add(a: URIO[Entity, Int])(b: URIO[Entity, Int]): URIO[Entity, Int] =
          a.zip(b).map { case (x, y) => x + y }
      }
    }
}

def add(a: URIO[Entity, Int])(b: URIO[Entity, Int]): URIO[Entity with Operation, Int] =
  ZIO.accessM(_.get[Service[URIO[Entity, *]]].add(a)(b))

這樣實體和操作可以像這樣組合:

operation.add(entity.int(5))(entity.int(37))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM