Generalized Function Programming (16) - Generalized Function State - FunctionalState


The first time I encountered a generalized function state I felt very uncomfortable. The main problem is that it is difficult to understand the rationale when using the State data type, especially the generic state transition mechanism: it is difficult to keep track of how the state changed. I think this is mostly because the state change mechanism has gone through function combinations and is buried deep behind the running code. Our discussion of the RNG in the last section was a good start to understanding State types. The RNG briefly describes the state change in a generic way and the data structures and operation function styles needed to support it.

In the previous section we mentioned type Rand[+A] = RNG => (A, RNG),Rand is a random number generating function。 thanks toRand It's a type, A function type, so it can be used as a parameter or return value。 Let's extend this definition a bit further, Get a little more versatile:type State[S, +A] = S => (A, S)。Rand evenState A special case of:type Rand[+A] = State[RNG, +A] 。 We callState is the state behavior, i.e.S => (A,S) is a function that defines the way the state changes。State The state change mechanism of the type is determined by the state behavior function。 Focus again on our designState Type of target:State Types not only allow us to encapsulate a lower-order type element like any other type and provide a set of state-changing mechanisms, And the state change mechanism is generalized, Naturally implicit。

Let's try a simple State type design first.

1 case class State[S,+A](run: S => (A, S)) 

sure!, It's that simple., And I did it on purpose.。 Note that the state behavior functionrun beState Internal members of a class, We purposefully put aState of the state change mechanism by building inState class when injected as a parameter。 And then the resultingState The instance will then perform the state change as we expect it to。case class Self-provided.apply, This allows us to directly use theState(???) establishState an actual example。 I'll putState(s => (a,s)) write asState { s => (a,s)}, It would be more natural to express that the incoming code is a piece of code。State[] Since it is a higher order type, Then we should also provide it with a set of functions to perform element operations inside the pipe。 bear in mind!! bear in mind!! While processing the values of the encapsulated elements within the pipe the type state has to be changed accordingly as required by the state behavior function。

Starting with the most basic components of the higher-order types.

1 object State {
2     def unit[S,A](a: A) = State[S,A](s => (a, s))
3 }

We touched on this UNIT earlier. It is an instance of State that encapsulates neither the value nor the state of the element to be transformed. The only function of unit is to raise the lower order level wrapper element type a to the State type.

Let's write a State function, remember! Remember! To address both the state change mechanism.

1 case class State[S,+A](run: S => (A, S)) {
2     def flatMap[B](f: A => State[S,B]): State[S,B] = State[S,B] {
3         s => {
4             val (a, s1) = run(s)
5             f(a).run(s1)
6         }
7     }

In flatMap we have processed the wrapper element a, f(a), with the function f. At the same time we refer back to the state behavior function run to perform a state change run(s) on the incoming state s.

1     def map[B](f: A => B): State[S,B] = State[S,B] {
2         s => {
3             val (a, s1) = run(s)
4             (f(a),s1)
5         }
6     }
7     def map_1[B](f: A => B): State[S,B] = {
8         flatMap { a => unit(f(a)) }
9     }

equivalent,map Also implementedf(a),run(s)。map You can also useflatMap come true。 The difference between them is simplyf: A => B harmony A => State[S,B]。 Because we do.unit, unit(a) = State[S,A],unit(f(a)) = State[S,B] So we useunit classifier for objects with a handlemap function parameters of theA Just upgrade it.。 usefulnessflatMap come truemap It is possible to putmap Pumping up to a higher level: thusmap Just ignore that state behavior function。

What about map2?

1     def map2[B,C](sb: State[S,B])(f: (A,B) => C): State[S,C] = {
2         flatMap {a => sb.map { b => f(a,b) }}
3     }
4     def map3[B,C,D](sb: State[S,B], sc: State[S,C])(f: (A,B,C) => D): State[S,D] = {
5         flatMap {a => sb.flatMap {b => sc.map { c => f(a,b,c) }}}
6     }

map2 function is to encapsulate the element type function with(A,B) => C Come and put twoState The elements in the tube combine。 We can applyflatMap Two times to combine the elements in the two tubes。 as far as sth is concernedmap3 We can add another one.。

An alternative expression for the continuous application of flatMap.

 1     def map2_1[B,C](sb: State[S,B])(f: (A,B) => C): State[S,C] ={
 2         for {
 3             a <- this
 4             b <- sb
 5         } yield f(a,b)
 6     }
 7     def map3_1[B,C,D](sb: State[S,B], sc: State[S,C])(f: (A,B,C) => D): State[S,D] ={
 8         for {
 9             a <- this
10             b <- sb
11             c <- sc
12         } yield f(a,b,c)
13     }

The syntatic sugar for-comprehension above allows us to enter a world of generalized letters as if with a sense of excitement. This form of expression is concise and straightforward, and much easier to understand. Again, there is no state change stuff involved in map2,map3. We achieve the invisible operation of state change.

The following is a practical example to demonstrate the generalized function state.

  1 //Stack type is a List[Int], which is easier to express a bit later
 2 type Stack = List[Int]
  3 //pop is a State instance. Its state behavior function is the partial function: splitting a ready-made List[Int] into new values and states
  4 //i.e., remove the first element and put it in the value
 5 def pop = State[Stack, Int]{ case x::xs => (x, xs) }
 6                                                   //> pop: => ch6.state.State[ch6.state.Stack,Int]
  7 //push is a State instance. Its state behavior function presses i onto a ready-made List[Int], which has nothing to do with the value
 8 def push(i: Int) = State[Stack, Unit]{ case xs => ((), i :: xs ) }
 9                                                   //> push: (i: Int)ch6.state.State[ch6.state.Stack,Unit]
10 def stackRun: State[Stack, Int] = {
11     for {
12         _ <- push(13)
13         a <- pop
14         b <- pop
15     } yield a+b
16 }                                                 //> stackRun: => ch6.state.State[ch6.state.Stack,Int]
17 
18 val (a, s) =stackRun.run(List(10,11,12))          //> a  : Int = 23
19                                                   //| s  : ch6.state.Stack = List(11, 12)

We don't mention the state Stack anywhere in stackRun, but look at the run result (a,s): not only is the return value correct, but the Stack state is silently transformed. Trying to analyze how the state shifts from the stackRun code is never going to be understood, so it's advisable to start from scratch honestly.

The generalized state is an invisible and automatic variation, so what do we do if we need to disrupt the established process and set or temporarily read the state manually?

1 object State {
2     def unit[S,A](a: A) = State[S,A](s => (a, s))
3     def getState[S]: State[S,S] = State[S,S] { s => (s,s) }
4   def setState[S](s: S): State[S,Unit] = State[S,Unit] { _ => ((),s)}
5     
6 }

It's still done through the state behavior function.

 1 def stackRun: State[Stack, Int] = {
 2     for {
 3         _ <- push(13)
 4         a <- pop
 5         _ <- setState(List(8,9))
 6         b <- pop
 7         s1 <- getState
 8     } yield (a + b)
 9 }                                                 //> stackRun: => ch6.state.State[ch6.state.Stack,Int]
10 
11 val (a, s) =stackRun.run(List(10,11,12))          //> a  : Int = 21
12                                                   //| s  : ch6.state.Stack = List(9)

We can temporarily set the state to List(8,9).


Recommended>>
1、Extension methods in net 30 example
2、java anonymous internal class
3、 NET 35 new features Lambda expressions
4、1 The basic system of game server development and some suggestions for serverside development
5、How good is the coworking product you have to look at these 25 indicators

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号