The rule is not really immutability, but referential transparency It's perfectly OK to use locally declared mutable variables and arrays, because none of the effects are observable to any other parts of the overall program.
The rule is not really immutability, but referential transparency. It's perfectly OK to use locally declared mutable variables and arrays, because none of the effects are observable to any other parts of the overall program. The principle of referential transparency (RT) is this: An expression e is referentially transparent if for all programs p every occurrence of e in p can be replaced with the result of evaluating e, without affecting the observable result of p.
Note that if e creates and mutates some local state, it doesn't violate RT since nobody can observe this happening. That said, I very much doubt that your implementation is any more straightforward with vars.
3 Yes. What you're looking for is some principle that, when followed, gives you some benefit. You had "immutability", which is pretty close, but too restrictive."Referential transparency" is the more general principle, and I believe it's the principle you're looking for.
– Apocalisp Dec 1 at 19:04 2 Also note that the principle you're looking for is not moderation. I.e. The idea that "a little" mutable state is OK.
– Apocalisp Dec 1 at 19:11 1 @Apocalisp I agree with the principle of what you're saying, but I don't feel it's entirely accurate. Using internal mutations still means that you're violating referential transparency within the local implementation. That can be a problem for all the same reasons if the local scope is large and complex enough (but it usually shouldn't be anyway).
It's just that you get most of the benefits by programming with referentially transparent interfaces. You seem to be implying that referential transparency is a property that can only be applied to interfaces, which isn't true. – Ben Dec 2 at 0:33 1 @Apocalisp Ah, then we agree.
I read your post as suggesting that if you went for "referential transparency everywhere" rather than "immutability everywhere", that you would naturally end up with pure interfaces and possibly impure local implementation. Although I believe it is possible for a function that is referentially transparent at its interface but uses mutability internally to fail when you have concurrency. If that's correct, then not all ways of using "local mutability" are truly unobservable.
– Ben Dec 2 at 4:36 2 @Ben: If concurrency can affect the result, then the expression is most emphatically not referentially transparent. – Apocalisp Dec 2 at 13:49.
The case for functional programming is one of being concise in your code and bringing in a more mathematical approach. It can reduce the possibility of bugs and make your code smaller and more readable. As for being easier or not, it does require that you think about your problems differently.
But once you get use to thinking with functional patterns it's likely that functional will become easier that the more imperative style. It is really hard to be perfectly functional and have zero mutable state but very beneficial to have minimal mutable state. The thing to remember is that everything needs to done in balance and not to the extreme.By reducing the amount of mutable state you end up making it harder to write code with unintended consequences.
A common pattern is to have a mutable variable whose value is immutable. This way identity ( the named variable ) and value ( an immutable object the variable can be assigned ) are seperate. Var acc: ListInt = Nil // lots of complex stuff that adds values acc ::= 1 acc ::= 2 acc ::= 3 // do loop current list acc foreach { I => /* do stuff that mutates acc */ acc ::= I * 10 } println( acc ) // List( 1, 2, 3, 10, 20, 30 ) The foreach is looping over the value of acc at the time we started the foreach.
Any mutations to acc do not affect the loop. This is much safer than the typical iterators in java where the list can change mid iteration. There is also a concurrency concern.
Immutable objects are useful because of the JSR-133 memory model specification which asserts that the initialization of an objects final members will occur before any thread can have visibility to those members, period! If they are not final then they are "mutable" and there is no guarantee of proper initialization. Actors are the perfect place to put mutable state.
Objects that represent data should be immutable. Take the following example. Object MyActor extends Actor { var acc: ListInt = Nil def act() { loop { react { case i: Int => acc ::= I case "what is your current value" => reply( acc ) case _ => // ignore all other messages } } } } In this case we can send the value of acc ( which is a List ) and not worry about synchronization because List is immutable aka all of the members of the List object are final.
Also because of the immutability we know that no other actor can change the underlying data structure that was sent and thus no other actor can change the mutable state of this actor.
Since Apocalisp has already mentioned the stuff I was going to quote him on, I'll discuss the code. You say it is just multiplying stuff, but I don't see that -- it makes reference to at least three important methods defined outside: order, states and M.log. I can infer that order is an Int, and that states return a function that takes a ListT and a T and returns Double.
There's also some weird stuff going on... def logLikelihood(seq: IteratorT): Double = { val sequence = seq. ToList sequence is never used except to define seqPos, so why do that? Val stateSequence = (0 to order).toList.
PadTo(sequence. Length,order) val seqPos = sequence. ZipWithIndex def probOfSymbAtPos(symb: T, pos: Int) : Double = { val state = states(stateSequence(pos)) M.
Log(state( seqPos. Map( _. _1 ).
Slice(0, pos). TakeRight(order), symb)) Actually, you could use sequence here instead of seqPos. Map( _.
_1 ), since all that does is undo the zipWithIndex. Also, slice(0, pos) is just take(pos). } val probs = seqPos.
Map( I => probOfSymbAtPos(i. _1,i. _2) ) probs.
Sum } Now, given the missing methods, it is difficult to assert how this should really be written in functional style. Keeping the mystery methods would yield: def logLikelihood(seq: IteratorT): Double = { import scala.collection.immutable. Queue case class State(index: Int, order: Int, slice: QueueT, result: Double) seq.
FoldLeft(State(0, 0, Queue. Empty, 0.0)) { case (State(index, ord, slice, result), symb) => val state = states(order) val partial = M. Log(state(slice, symb)) val newSlice = slice enqueue symb State(index + 1, if (ord == order) ord else ord + 1, if (queue.
Size > order) newSlice.dequeue. _2 else newSlice, result + partial) }. Result } Only I suspect the state/M.
Log stuff could be made part of State as well. I notice other optimizations now that I have written it like this. The sliding window you are using reminds me, of course, of sliding: seq.
Sliding(order).zipWithIndex. Map { case (slice, index) => M. Log(states(index + order)(slice.
Init, slice. Last)) }. Sum That will only start at the orderth element, so some adaptation would be in order.
Not too difficult, though. So let's rewrite it again: def logLikelihood(seq: IteratorT): Double = { val sequence = seq. ToList val slices = (1 until order).
Map(sequence take) ::: sequence. Sliding(order) slices.zipWithIndex. Map { case (slice, index) => M.
Log(states(index)(slice. Init, slice. Last)) }.
Sum } I wish I could see M. Log and states... I bet I could turn that map into a foldLeft and do away with these two methods. And I suspect the method returned by states could take the whole slice instead of two parameters.Still... not bad, is it?
Thx for the review. Helped a lot. – peri4n Dec 1 at 21:47.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.