【问题标题】:Scalaz: combine Writer and State (and/or Lens)Scalaz:结合 Writer 和 State(和/或 Lens)
【发布时间】:2017-03-28 15:48:56
【问题描述】:

我正在尝试结合 Writer 和 State(通过 Lens)。我很确定我需要 monad 转换器,但我很难弄清楚如何使用 T 版本以及如何正确构建它。

现在我有一些模型(简化):

case class Schedule(due: LocalDate)
case class Task(title: String, schedule: Schedule)

为每个字段定义的镜头,titleLscheduleLdueL

我的 Writer type Logger[A] = Writer[Vector[String], A] 的类型别名

还有一些修改我的模型的功能:

def changeTitle(title: String): Task => Logger[Task] = { t: Task =>
  for {
    a <- titleL.set(t, title).point[Logger]
    _ <- ("Title changed to " + a.title).point[Vector].tell whenM (a.title != t.title)
  } yield a
}

def changeDue(date: LocalDate): Schedule => Logger[Schedule] = { s: Schedule =>
  for {
    a <- dueL.set(s, date).point[Logger]
    _ <- ("Due changed to " + a.due).point[Vector].tell whenM (a.due != s.due)
  } yield a
}

但是现在我不确定如何将 lens 或 state 方法与最后一个函数一起使用。

我希望能够做一些看起来像这样的事情:

def reschedule(date: LocalDate): Task => Logger[Task] = { t: Task =>
  (for {
    a <- scheduleL %= reschedule(date)
    _ <- ("Reschedule: " + a.schedule).point[Vector].tell whenM (a.schedule != t.schedule)
  } yield a) exec t
}

我应该如何处理这个问题?我在单子变压器的正确轨道上吗?还有什么我可能遗漏的已经处理了我的案子的事情吗?


编辑:

我得到了这样的东西,它适用于那个用例,但我想要一些能更好地与 State 集成以用于更复杂的东西:

def reschedule(date: LocalDate): Task => Logger[Task] = { t: Task =>
  for {
    sa <- scheduleL.get(t).point[Logger]
    sb <- changeDue(date)(sa)
    a <- scheduleL.set(t, sb).point[Logger]
    _ <- ("Reschedule: " + a.schedule).point[Vector].tell whenM (a.schedule != t.schedule)
  } yield a
}

【问题讨论】:

    标签: scala monads scalaz


    【解决方案1】:

    也许你可以进一步简化它,但这是我能得到的最好的。 据我所知,您不能直接在 monad 转换器中使用 lens,但您可以将 lens 转换为状态,这接近您的需要。

    首先让我们定义我们的 monad。

    def RWA[R, A] = ReaderWriterState.rwstMonad[Id.Id, R, Vector[String], A]
    val RST = RWA[String, Task]
    val RLS = RWA[Long, Schedule]
    
    
      def changeTitleV1 = for {
        title ← RST.ask // Reader part of transformer getting new title from the environment
        _ ← RST.modify(titleL =>= (_ ⇒ title))  // `=>=` is converting lens to `A => A`
        _ ← RST.tell(Vector(s"I put the value $title")))
      } yield ()
    
    
    changeTitleV1.run("new title", Task("old title", Schedule(123)))  //(Vector(I put the value new title),(),Task(new title,Schedule(123)))
    

    我们将我们的新标题作为第一个参数传递给这个 run 函数,以便能够在 monad 中询问它。

    如您的示例 - 您希望在日志中写入特定条件,因此您需要获取初始状态以了解标题是否已更改。它变得不那么简洁了:

    def changeTitleV2 = for {
        title ← RST.ask
        task0 ← RST.get
        _ ← RST.put(titleL.set(task0, title))
        _ ← RST.whenM(task0.title != title)(RST.tell(Vector(s"I put the value $title")))
      } yield ()
    

    当然你也可以为changeDue定义相同的:

      def changeDue = for {
        d0 ← RLS.get
        due ← RLS.ask
        _ ← RLS.put(dueL.set(t0, due))
        _ ← RLS.whenM(d0.due != due)(RLS.tell(Vector(s"due changed to $due")))    
      } yield ()
    

    也就是说,我不太确定,您提出的解决方案是否更好。

    【讨论】:

    • 正如你所说,它看起来确实与我最终得到的相似,但实际上我开始发现,当我了解更多时,我可能没有正确地对整个事物进行建模。不过,我有一种感觉 RWS 会出现,所以我会保留它作为示例,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多