2016-11-14 18 views
3

Я пытаюсь объединить Writer и State (через объектив). Я уверен, что мне нужны трансформаторы монады, но мне сложно понять, как использовать версии T и как правильно их строить.Scalaz: comb Writer and State (and/or Lens)

Сейчас у меня есть некоторые модели (упрощенные):

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

линза, определенная для каждого поля, titleL, scheduleL и dueL.

Типа псевдоним для моего писателя 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 
} 

Однако теперь я не уверен, как использовать линзы или государственные методы с этой последней функцией.

Я хотел бы быть в состоянии сделать что-то, что будет выглядеть вроде этого:

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 
} 

Как я должен подойти к этому? Я на правильном пути с монадными трансформаторами? Что-нибудь еще, что я мог пропустить, уже обрабатывает мое дело?


EDIT:

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

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 
} 

ответ

0

Может быть, вы можете упростить его дальше, но это лучшее, что я могу получить. Насколько я знаю, вы не можете использовать объектив в монодальном трансформаторе напрямую, но вы можете конвертировать объектив в состояние, близкое к тому, что вам нужно.

Сначала давайте определим наши монады.

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, чтобы иметь возможность спросить его внутри монады.

Как и в вашем примере - вы хотите иметь определенное условие для записи в журнале, поэтому вам нужно получить начальное состояние, чтобы узнать, было ли изменено название. Она становится менее кратким:

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() 

То есть, я не уверен, что это гораздо лучше, что решение, которое вы пришли с.

+0

Как вы сказали, это похоже на то, с чем я закончил, но на самом деле я начинаю открывать, когда узнаю больше, что я, вероятно, не смоделировал все правильно. У меня было ощущение, что RWS появится, хотя я продолжу это в качестве примера, спасибо за это. – wiill