co to jest właściwa monada lub rozumienie sekwencji, aby mapować i przenosić stan w poprzek?
Piszę interpreter języka programowania.
Potrzebuję odpowiedniego idiomu kodu, aby zarówno ocenić sekwencję wyrażeń, aby uzyskać sekwencję ich wartości, jak i propagować stan od jednego ewaluatora do następnego obok, gdy ewaluacje mają miejsce. Chciałbym do tego funkcjonalny idiom programowania.
To nie jest fałd, ponieważ wyniki wyglądają jak mapa. To nie jest mapa z powodu podpory stanu.
Mam ten kod, którego używam, aby spróbować to rozgryźć. Najpierw zrób kilka linii stanowiska testowego:
// test rig
class MonadLearning extends JUnit3Suite {
val d = List("1", "2", "3") // some expressions to evaluate.
type ResType = Int
case class State(i : ResType) // trivial state for experiment purposes
val initialState = State(0)
// my stub/dummy "eval" function...obviously the real one will be...real.
def computeResultAndNewState(s : String, st : State) : (ResType, State) = {
val State(i) = st
val res = s.toInt + i
val newStateInt = i + 1
(res, State(newStateInt))
}
Moje obecne rozwiązanie. Używa var, który jest aktualizowany, gdy ciało mapy jest oceniane:
def testTheVarWay() {
var state = initialState
val r = d.map {
s =>
{
val (result, newState) = computeResultAndNewState(s, state)
state = newState
result
}
}
println(r)
println(state)
}
Używam foldLeft, który uważam za rozwiązania, które uważam za nieakceptowalne.
def testTheFoldWay() {
// This startFold thing, requires explicit type. That alone makes it muddy.
val startFold : (List[ResType], State) = (Nil, initialState)
val (r, state) = d.foldLeft(startFold) {
case ((tail, st), s) => {
val (r, ns) = computeResultAndNewState(s, st)
(tail :+ r, ns) // we want a constant-time append here, not O(N). Or could Cons on front and reverse later
}
}
println(r)
println(state)
}
Mam też kilka wariacji rekurencyjnych (które są oczywiste, ale też nie są jasne lub dobrze zmotywowane), jedną wykorzystującą strumienie, które są prawie tolerowane:
def testTheStreamsWay() {
lazy val states = initialState #:: resultStates // there are states
lazy val args = d.toStream // there are arguments
lazy val argPairs = args zip states // put them together
lazy val resPairs : Stream[(ResType, State)] = argPairs.map{ case (d1, s1) => computeResultAndNewState(d1, s1) } // map across them
lazy val (results , resultStates) = myUnzip(resPairs)// Note .unzip causes infinite loop. Had to write my own.
lazy val r = results.toList
lazy val finalState = resultStates.last
println(r)
println(finalState)
}
Ale nie mogę wymyślić niczego tak zwięzłego ani jasnego, jak oryginalne rozwiązanie „var”, z którym chciałbym żyć, ale myślę, że ktoś, kto je / pije / śpi w monotonnych idiomach, po prostu powie ... . użyj tego ... (Mam nadzieję!)