TL; DR
вы можете использовать traverse
из класса типов Traverse
для коллекции (например, List
) CDR
с,используя функцию с такой подписью: CDR => State[Network, Long]
.Результатом будет State[Network, List[Long]]
.В качестве альтернативы, если вас не волнует List[Long]
, вы можете вместо этого использовать traverse_
, что вернет State[Network, Unit]
.Наконец, если вы хотите «агрегировать» результаты T
по мере их поступления, и T
образует Monoid
, вы можете использовать foldMap
из Foldable
, который вернет State[Network, T]
, где T
- это объединенный (например, сложенный) результат всех T
s в вашей цепочке.
Пример кода
Теперь немного подробнее, с примерами кода.Я отвечу на это, используя Cats State
, а не Scalaz, поскольку я никогда не использовал последний, но концепция та же самая, и, если у вас все еще есть проблемы, я найду правильный синтаксис.
Предположим, что у нас есть следующие типы данных и импорт для работы:
import cats.implicits._
import cats.data.State
case class Position(x : Int = 0, y : Int = 0)
sealed trait Move extends Product
case object Up extends Move
case object Down extends Move
case object Left extends Move
case object Right extends Move
Как ясно, Position
представляет точку в 2D-плоскости, и Move
может перемещатьсятакая точка вверх, вниз, влево или вправо.
Теперь давайте создадим метод, который позволит нам увидеть, где мы находимся в данный момент времени:
def whereAmI : State[Position, String] = State.inspect{ s => s.toString }
и метод для изменениянаша позиция, с учетом Move
:
def move(m : Move) : State[Position, String] = State{ s =>
m match {
case Up => (s.copy(y = s.y + 1), "Up!")
case Down => (s.copy(y = s.y - 1), "Down!")
case Left => (s.copy(x = s.x - 1), "Left!")
case Right => (s.copy(x = s.x + 1), "Right!")
}
}
Обратите внимание, что это вернет String
с названием хода, за которым следует восклицательный знак.Это просто для того, чтобы смоделировать изменение типа с Move
на что-то другое, и показать, как будут агрегированы результаты.Подробнее об этом чуть позже.
Теперь давайте попробуем поиграть с нашими методами:
val positions : State[Position, List[String]] = for{
pos1 <- whereAmI
_ <- move(Up)
_ <- move(Right)
_ <- move(Up)
pos2 <- whereAmI
_ <- move(Left)
_ <- move(Left)
pos3 <- whereAmI
} yield List(pos1,pos2,pos3)
И мы можем дать ему начальное значение Position
и посмотреть результат:
positions.runA(Position()).value // List(Position(0,0), Position(1,2), Position(-1,2))
(вы можете игнорировать .value
там, это странно из-за того, что State[S,A]
действительно просто псевдоним для StateT[Eval,S,A]
)
Как вывидите, это ведет себя так, как вы ожидаете, и вы можете создавать различные «чертежи» (например, последовательности изменений состояния), которые будут применяться после предоставления начального состояния.
Теперь, чтобы фактически ответить вамвопрос, скажем, у нас есть List[Move]
, и мы хотим применить их последовательно к начальному состоянию и получить результат: мы используем traverse
из Traverse
типа класса .
val moves = List(Down, Down, Left, Up)
val result : State[Position, List[String]] = moves.traverse(move)
result.run(Position()).value // (Position(-1,-1),List(Down!, Down!, Left!, Up!))
В качестве альтернативы, если вам вообще не нужен A
(в вашем случае List
), вы можете использовать traverse_
вместо traverse
, и тип результата будет:
val result_ : State[Position, List[String]] = moves.traverse_(move)
result_.run(Position()).value // (Position(-1,-1),Unit)
Наконец, если ваш A
тип в State[S,A]
образует Monoid
, то вы также можете использовать foldMap
из Foldable
для объединения (например, fстарые) все A
с, как они рассчитаны.Тривиальный пример (вероятно, бесполезный, потому что это просто объединит все String
с) будет следующим:
val result : State[Position,String] = moves.foldMap(move)
result.run(Position()).value // (Position(-1,-1),Down!Down!Left!Up!)
Является ли этот последний подход полезным или нет для вас, действительно зависит от того, что A
выиметь и, если имеет смысл объединить это.
И это должно быть все, что вам нужно в вашем сценарии.