Когда я изучаю State Monad
, я не знаю, как скомпоновать две функции с разными типами возвращаемого значения State
.
Определение государственной монады:
case class State[S, A](runState: S => (S, A)) {
def flatMap[B](f: A => State[S, B]): State[S, B] = {
State(s => {
val (s1, a) = runState(s)
val (s2, b) = f(a).runState(s1)
(s2, b)
})
}
def map[B](f: A => B): State[S, B] = {
flatMap(a => {
State(s => (s, f(a)))
})
}
}
Два разных типа состояния:
type AppendBang[A] = State[Int, A]
type AddOne[A] = State[String, A]
Два метода с разными типами возвращаемого состояния:
def addOne(n: Int): AddOne[Int] = State(s => (s + ".", n + 1))
def appendBang(str: String): AppendBang[String] = State(s => (s + 1, str + " !!!"))
Определите функцию для использования двух функций выше:
def myAction(n: Int) = for {
a <- addOne(n)
b <- appendBang(a.toString)
} yield (a, b)
И я надеюсь использовать его так:
println(myAction(1))
Проблема в том, что myAction
не компилируется, выдает такую ошибку:
Error:(14, 7) type mismatch;
found : state_monad.State[Int,(Int, String)]
required: state_monad.State[String,?]
b <- appendBang(a.toString)
^
Как я могу это исправить? Должен ли я определять некоторые преобразователи Monad?
Обновление: вопрос может быть не ясен, позвольте мне привести пример
Скажем, я хочу определить другую функцию, которая внутри использует addOne
и appendBang
. Поскольку всем им нужны существующие состояния, я должен передать им некоторые из них:
def myAction(n: Int)(addOneState: String, appendBangState: Int): ((String, Int), String) = {
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState(appendBangState)
((addOneState2, appendBangState2), n3)
}
Я должен запускать addOne
и appendBang
один за другим, передавая и получая состояния и результат вручную.
Хотя я обнаружил, что он может вернуть еще одно State
, код не сильно улучшился:
def myAction(n: Int): State[(String, Int), String] = State {
case (addOneState: String, appendBangState: Int) =>
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState( appendBangState)
((addOneState2, appendBangState2), n3)
}
Поскольку я не совсем знаком с ними, просто интересно, есть ли способ улучшить его. Лучше всего надеяться, что я смогу использовать for
понимание, но не уверен, что это возможно
flatMap
иfor
понимания, если бы вы определили две функции приведенияasFirst[A](State[B, X]): State[(A,B), X]
иasSecond[B](State[A, X]): State[(A,B), X]
(или около того)? Если да, то какой самый идиоматический способ сделать это (я плохо разбираюсь в Scala)? 06.09.2015onFirst
иonSecond
более разумные имена. Дело не в том, что мы воспринимаем результаты как что-то, а в том, что мы оперируем на частью состояния. 09.09.2015