Kilka tygodni temu Dragisa Krsmanovic zapytał a question here o to, jak korzystać z bezpłatnej monady w Scalaz 7, aby uniknąć przepełnienia stosu w tej sytuacji (trochę zaadaptowałem jego kod):Applicative vs. Monadic Combinators i bezpłatna monada w Scalaz
import scalaz._, Scalaz._
def setS(i: Int): State[List[Int], Unit] = modify(i :: _)
val s = (1 to 100000).foldLeft(state[List[Int], Unit](())) {
case (st, i) => st.flatMap(_ => setS(i))
}
s(Nil)
Myślałem that just lifting a trampoline into StateT
powinno działać:
import Free.Trampoline
val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) {
case (st, i) => st.flatMap(_ => setS(i).lift[Trampoline])
}
s(Nil).run
Ale to wciąż wieje stos, więc po prostu pisał je jako komentarz.
Dave Stevens tylko pointed out że sekwencjonowanie z aplikacyjnej *>
zamiast monadycznej flatMap
faktycznie działa dobrze:
val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) {
case (st, i) => st *> setS(i).lift[Trampoline]
}
s(Nil).run
(Cóż, to bardzo wolno, oczywiście, bo to cena, jaką płacą za to nic ciekawego jak to w Scali, ale przynajmniej nie ma przepełnienia stosu.)
Co tu się dzieje? Nie sądzę, aby istniała podstawowa przyczyna tej różnicy, ale tak naprawdę nie mam pojęcia, co może się dziać w tej implementacji i nie mam czasu, aby w tej chwili kopać. Ale jestem ciekawy i byłoby fajnie, gdyby ktoś inny wiedział.
+1 Dzięki, ale rozumiem, że droga została trampolinie jest 'flatMap' reifies bind na stercie oznacza, że będziemy tu bezpieczni nawet gdybyśmy nie odrzucili tego wyniku? –
@cdk Nie sądzę, że to jest odpowiedź. Wybierz innego operatora, który zależy od wyniku po lewej * i * wymaga "Zastosuj" zamiast "Bind". na przykład '| @ |' https://gist.github.com/drstevens/3ea464446ee59463af1e – drstevens