Стандартный способ объединения состояний в скаляр
Предположим, у вас есть Nel состояний (Nel означает NonEmptyList, чтобы сделать вещи короче), и вы хотите объединить состояния в одно состояние, используя некоторую функцию f для левой части состояния и g для правой части государство.
Итак, вы хотите что-то вроде этого:
def foldStatesG[S, A](in: NonEmptyList[State[S, A]])(f: (A, A) => A)(g: (S, S) => S): State[S, A] = {
in.foldLeft1((s1, s2) => State(e => {
val ss1 = s1(e)
val ss2 = s2(e)
g(ss1._1, ss2._1) -> f(ss1._2, ss2._2)
}))
}
Я уверен, что я изобретаю велосипед, и такая вещь уже существует, возможно, в более общем смысле. Но, просматривая скалаз, я не смог найти или распознать его. Был бы признателен за любую помощь по этой теме.
Второй вопрос, описывает, как я пришел к такой проблеме. Я хотел сделать небольшой симулятор, когда у вас есть враг (считайте, что это просто двойник) и все возможные заклинания, которые могут поразить его, Нел [Заклинание]. В общем, я хочу создать все возможные последовательности. Например, если Nel[Spell] = ($, #), то с учетом врага E прогрессия будет выглядеть
E -> (), then Nel(E -> $, E -> #), then Nel(E -> $$, E -> ##, E -> $#, E-> #$) etc.. (pseudo code)
Не вдаваясь в подробности, мне нужно что-то вроде этого:
def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], Boolean]
Другими словами, вы предоставляете ему все возможные ходы, и он имитирует все возможные состояния. Вы можете рассматривать это как своего рода дерево решений, которое ветвится на каждом шагу. Отсюда я определил, как действовать одним заклинанием.
def combineS1(spell: Spell): State[(NonEmptyList[(Enemy, List[Spell])]), Boolean]
// some logic
Проблема в том, что я не могу реализовать это с помощью простого обхода:
def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], Boolean] = {
val x = variations traverse combine1 // State[Nel[..], Nel[Boolean]
x map { _.suml1(conjunction)}
}
Потому что при прохождении Nel s не присоединяются друг к другу, а отбрасываются. Вот почему я придумал это решение:
def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], AllDead] = {
val x: NonEmptyList[State[NonEmptyList[(Enemy, List[Spell])], Boolean]] = variations map combineS
foldStates(x)(_ && _)(append)
}
Этот код на самом деле работает, но я чувствую, что есть способ обойти его без дополнительного foldState.
Какими бы были другие функциональные подходы для такого моделирования (по крайней мере, с точки зрения концепций). Я могу написать это просто функционально, без использования концепций высокого уровня, но цель этого упражнения состояла именно в том, чтобы изучить продвинутые вещи. (Может быть, это именно работа для писателя Монада? Или Комонад?)
Также, если stackru не является лучшим местом для таких вопросов, пожалуйста, укажите, где такие вопросы должны задаваться?
1 ответ
Ну, чтобы ответить на первую часть этого, если бы мы были в альтернативной вселенной, где Скалаз уже имел Biapplicative
Тип класса, мы могли бы использовать его следующим образом:
def foldStatesG[S, A](states: NonEmptyList[State[S, A]], s: (S, S) ⇒ S, a: (A, A) ⇒ A): State[S, A] =
states.foldLeft1(IndexedStateT.indexedStateTBiapplicative[Id, S].bilift2(s, a))
Но мы этого не делаем, поэтому нам нужно что-то вроде следующего (примечание: я не уверен, что это делает что-то вменяемое с точки зрения закона):
trait Biapplicative[F[_, _]] extends Bifunctor[F] {
def bipure[A, B](a: ⇒ A, b: ⇒ B): F[A, B]
def biapply[A, B, C, D](fab: ⇒ F[A, B])(f: ⇒ F[A ⇒ C, B ⇒ D]): F[C, D]
def bilift2[A, B, C, D, E, G](fab: (A, B) ⇒ C, fde: (D, E) ⇒ G): (F[A, D], F[B, E]) ⇒ F[C, G] =
(fad: F[A, D], fbe: F[B, E]) ⇒
biapply(fbe)(bimap[A, D, B ⇒ C, E ⇒ G](fad)(fab.curried, fde.curried))
}
object Biapplicative {
implicit def indexedStateTBiapplicative[F[_], S1](implicit F: Applicative[F]) =
new Biapplicative[IndexedStateT[F, S1, ?, ?]] {
def bipure[A, B](a: ⇒ A, b: ⇒ B) =
StateT.constantIndexedStateT(b)(a)
def biapply[A, B, C, D]
(fab: ⇒ IndexedStateT[F, S1, A, B])
(f: ⇒ IndexedStateT[F, S1, A ⇒ C, B ⇒ D]) =
new IndexedStateT[F, S1, C, D] {
def apply(initial: S1): F[(C, D)] =
F.ap(fab(initial))(F.map(f(initial)) {
case (a2c, b2d) ⇒ Bifunctor[Tuple2].bimap(_)(a2c, b2d)
})
}
}
}