-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
166 lines (138 loc) · 5.33 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
The Agent class was strongly inspired by the agent principle in Clojure. Essentially, an agent wraps a shared mutable state and hides it behind a message-passing interface.
Agents accept messages and process them on behalf of the wrapped state. Typically agents accept functions / commands as messages and ensure the submitted commands are executed against the internal agent's state in a thread-safe manner (sequentially). The submitted functions / commands take the internal state as a parameter and their output becomes the new internal state value.
The code that is submitted to an agent doesn't need to pay attention to threading or synchronization, the agent will provide such guarantees by itself.
See the examples of use for more details.
/**
* Stores a list of names and allows concurrent threads to manipulate the list.
*/
object AgentSample {
def main(args: Array[String]) {
val agent = Agent(List("Joe"))
agent start()
agent(List("Dave")) //Set the state to a new value
agent((x: List[String]) => "Alice" :: x) //Pre-pend a value to the internal list
agent("Susan" :: _) //Pre-pend a value to the internal list
agent get(_.foreach(println _)) //Print the content asynchronously
}
}
/**
* Wraps a counter allowing other threads to safely update the counter concurrently
*/
object CounterSample {
def increment(x: Long) = x + 1
def decrement(amount : Long)(x: Long) = x - amount
def main(args: Array[String]) {
val agent = Agent(0L) //Wrap a counter by an Agent
agent start
agent((x: Long) => x + 100) //Increment the value by 100
agent{_ + 100} //Increment the value by 100
def a = actor {
agent(_ + 100) //Increment the value by 100 from within an actor
}
a.start
new Thread(new Runnable() { //Start a new thread to manipulate the counter in parallel
def run = {
agent(increment(_)) //Increment the value by 1 calling the increment function
}
}).start
agent(increment _) //Send a method that will increment the counter by 1
agent(decrement(3) _) //Send a method that will decrement the counter by 3
println(agent get) //Print the content
}
}
/**
* A thread-safe shopping cart wrapping the internal state represented as a HashMap inside an Agent.
* All public method calls on the ShoppingCart are transformed into a command execution on the internal agent.
*/
class ShoppingCart {
val content = Agent(Map[String, Int]())
def start() { content start}
def getContent() : Map[String, Int] = { content.get }
def addItem(product: String) {
content((x: Map[String, Int]) => {
x + product -> 1
})
}
def removeItem(product: String) {
content((x: Map[String, Int]) => {
x - product
})
}
def clear() {
content((x: Map[String, Int]) => {
Map[String, Int]()
})
}
/**
* Curry the changeQuantity() private method and send it off to the Agent for processing
*/
def increaseQuantity(product:String, quantity:Int) {
content(changeQuantity(product, quantity, _:Map[String, Int]))
}
/**
* Manipulates the cart's map directly, since it is never called directly by clients, but always indirectly
* inside the Agent's thread.
*/
private def changeQuantity(product:String, quantity:Int, items:Map[String, Int]) = {
if (items.contains(product)) {
def currentQuantity = items(product)
items + product -> (currentQuantity + quantity)
} else items
}
}
object ShoppingCartExample {
def main(args: Array[String]) {
val cart: ShoppingCart = new ShoppingCart
cart start
cart addItem "Budweiser"
cart addItem "Pilsner"
cart addItem "Staropramen"
cart removeItem "Budweiser"
println(cart getContent)
cart increaseQuantity("Staropramen", 5)
println(cart getContent)
cart clear()
println(cart getContent)
}
}
/**
* Uses a custom copy strategy to avoid returning the original state instance.
*/
object CustomCopyStrategyExample {
object MyIntCopyStrategy extends CopyStrategy[Int] {
def apply(x:Int) : Int = {
def a = x
return a
}
}
def main(args: Array[String]) {
val agent = Agent(0, MyIntCopyStrategy)
agent start
agent(_ + 10)
agent(_ + 20)
println(agent get)
}
}
/**
* Shows (after un-commenting) the default error handling mechanics.
*/
object NumberExample {
def main(args: Array[String]) {
val agent = Agent(List(10))
agent start
agent() = List(1)
agent() = (x: List[Int]) => 2 :: x
agent((x: List[Int]) => 3 :: x)
agent((x: List[Int]) => x.reverse)
// agent((x: List[Int]) => {
// if (true) throw new RuntimeException("test")
// return List()
// })
agent((x: List[Int]) => {
x.map(_ * 2)
})
// agent.get((x: List[Int]) => {throw new RuntimeException("read test")})
agent.get((x: List[Int]) => {println("Value: " + x)})
println("Result: " + agent())
}
}