-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
312 lines (229 loc) · 18.4 KB
/
index.xml
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Eric Weise</title>
<link>http://eweise.com/index.xml</link>
<description>Recent content on Eric Weise</description>
<generator>Hugo -- gohugo.io</generator>
<language>US-EN</language>
<copyright>All rights reserved - 2017</copyright>
<lastBuildDate>Wed, 18 Sep 2019 22:00:06 -0800</lastBuildDate>
<atom:link href="http://eweise.com/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Category Theory in Kotlin - Monoids</title>
<link>http://eweise.com/post/category-into/</link>
<pubDate>Wed, 18 Sep 2019 22:00:06 -0800</pubDate>
<guid>http://eweise.com/post/category-into/</guid>
<description>
<h2 id="introduction">Introduction</h2>
<p>I am starting a series of blog posts pertaining to a book that I am reading called <a href="https://github.com/hmemcpy/milewski-ctfp-pdf">Category Theory for Programmers</a> by Bartosz Milewski.
Bartosz has already done a series a <a href="https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface">posts</a> on the topics in the book so instead of covering the same ground, my posts will be specifically about implementing the various category pattern in kotlin.
I will try to make them as easy to understand as possible and provide useful examples.
I won&rsquo;t go into detail about actual category theory because the book does a much better job of that.
I will just take a look at the pattern itself and explain why its useful and show an example in Kotlin</p>
<h2 id="monoids">Monoids</h2>
<p>Monoid is our first pattern. The name sounds like some disease you might catch or aliens from another planet.
But like most names from category theory, it might sound a bit intimidating but the actual pattern is simple to understand.
Let&rsquo;s take a look at what a Monoid is</p>
<pre><code class="language-java">interface Monoid&lt;T&gt;{
fun combine(t1:T, t2:T):T
fun empty():T
}
</code></pre>
<p>There are two methods. The first is <em>combine</em> and it does pretty much what it says. It combines two values represented by <em>&lt;T&gt;</em>.
The next function <em>empty</em> is less obvious. It takes no parameters and just returns a <em>T</em>.
The return value however, need to represent a value that when <em>combined</em> with another <em>T</em>, will not change the other value.
For example if I combine the value 3 with the value 0 then the result still 3.</p>
<p>There is one more aspect to Monoid and category theory patterns in general. In addition to specifying an interface, they also have laws.
The law with Monoid is that the combine function must be associative, meaning that if I combine values A and B first and then combine that result with C,
it should be the same as combining values B and C first and then combining A with that result. The order of precedence can change without changing the result.
In math terms we say: (A + B) + C = A + (B + C). This law can&rsquo;t be enforced by the compiler but users of your Monoid will expect it to be true.</p>
<p>Let&rsquo;s see the power of monoids by applying the pattern to a custom data type Money. Here is our simple Money type</p>
<pre><code class="language-java">data class Money(val value: BigDecimal) {
constructor(strValue: String) : this(BigDecimal(strValue))
}
</code></pre>
<p>It would be great if we could add our money so let&rsquo;s create a Money Monoid.</p>
<pre><code class="language-java">object MoneyMonoid : Monoid&lt;Money&gt; {
override fun combine(v1: Money, v2: Money): Money {
return Money(v1.value.plus(v2.value))
}
override fun empty(): Money {
return Money(BigDecimal.ZERO)
}
}
</code></pre>
<p>Notice that the empty() method is implemented by returning the value 0.</p>
<p>Here is an example of our MoneyMonoid in action</p>
<pre><code class="language-java">assert(MoneyMonoid.combine(Money(&quot;1&quot;), Money(&quot;2&quot;)) == Money(&quot;3&quot;))
</code></pre>
<p>The MoneyMonoid succesfully added 1 + 2 to equal 3.
OK that&rsquo;s not so impressive since we could have just directly put an add function on the Money class.
The power of the abstraction however, can be demonstrated by building additional functionality on top of Monoid.
For instance, we can now create an extension method <em>fold</em> that combines any number of Monoids.</p>
<pre><code class="language-java">fun &lt;T&gt; Monoid&lt;T&gt;.fold(ts:List&lt;T&gt;):T {
return ts.foldRight(this.empty()) { t: T, accumulator: T -&gt; this.combine(t, accumulator) }
}
</code></pre>
<p>Notice that the <em>fold</em> function could be used with any Monoid, not just MoneyMonoid.</p>
<p>Now we can use this generic fold method to sum all our money</p>
<pre><code class="language-java">val listOfMoney = listOf( Money(&quot;1&quot;),Money(&quot;2&quot;),Money(&quot;3&quot;) )
assert(MoneyMonoid.fold(listOfMoney) == Money(&quot;6&quot;))
</code></pre>
<p>We defined a very generic concept <em>combining</em> and were able to build upon it to create additional functionality.
category theory patterns are all very generic and sometimes very abstract.
They are built using interfaces but also have laws that must be enforced by each implementation.</p>
</description>
</item>
<item>
<title>Pushing Side Effects to the Side</title>
<link>http://eweise.com/post/sideeffects/</link>
<pubDate>Mon, 12 Jun 2017 22:00:06 -0800</pubDate>
<guid>http://eweise.com/post/sideeffects/</guid>
<description><p>Functional programming provides a treasure trove of useful patterns, one of which is pushing side effects to the edges of your application. Side effects are functions that have non-deterministic behavior. Examples include making an Http call or persisting data to the database These calls can fail due to network issues or other outside influences. But side effects can also be functions that don&rsquo;t fail such as retrieving the current time or a random number, since they don&rsquo;t always give the same value back. By isolating these types of calls, we can more easily test our code.</p>
<p>The following is an example loosely based on a refactoring I did at work. The use case is similar to Google Calendar where events re-occur over a period of time or for a number of iterations. An example code snippet from our implementation looked similar to the following;</p>
<pre><code class="language-java">class EventService {
public List&lt;RecurringEvent&gt; generateEvents(RecurringEvent recurringEvent) {
List&lt;Event&gt; generatedEvents = new ArrayList&lt;&gt;();
LocalDate nextDate = recurringEvent.startDate;
int generatedCount = 0;
LocalDate maximumDate = Local.now().plusDays(180);
while(generatedCount &lt; recurringEvent.maxNumber
&amp;&amp; !nextDate.isAfter(recurringEvent.endDate))
&amp;&amp; !nextDate.isAfter maximumDate) {
Event newEvent = createEvent(recurringEvent, nextDate);
entityManager.persist(newEvent);
generatedEvents.add(newEvent);
generatedCount++;
nextDate = Recurrence.nextDate(nextDate, recurringEvents.repeatPattern);
}
return generatedEvents;
}
}
</code></pre>
<p>This code has some non-trivial conditions that determines how many events to generate. Testing it would involve checking each of the conditions. At a minimum however, we can test that it generates some events.</p>
<pre><code class="language-java">
class EventServiceTest extends SpringyTest {
@Autowired
private EntityManager EntityManager;
private EventService eventService = new EventService(entityManager);
@test
public void testgenerateEvents() {
RecurringEvent recurringEvent = RecurringEventFixture.create();
List&lt;Event&gt; generatedEvents = eventService.generateEvents(recurringEvents);
// assert the correct number of events were generated
Assert.assertEquals(4, generateEvents.size());
}
}
</code></pre>
<p>While the test looks fairly straightforward, it is actually complex because it needs to connect to a database in order to succeed. We need to ensure that the database starts and ends in a clean state and that no other tests interfere with the data while the test is running. Otherwise, we may get a different result or db related failure. The test also incurs a large performance penalty since it needs to wire up a connection pool and any other dependencies in the Spring configuration.</p>
<p>In the refactored code, the EntityManager.persist call is removed from the business logic. Notice that the LocalDate.now call is also removed. Remember that we want to always get the same result given the same method argument. Having the current date directly in the business logic could cause the function to behave differently depending on what day it is. Have you ever had tests that worked fine most days but failed at the beginning or end of the month? Moving the dates outside of the business logic should help.</p>
<pre><code class="language-java">class EventService {
public void generateAndPersistRecurringEvents(RecurringEvent recurringEvent) {
List&lt;RecurringEvent&gt; newEvents = generateEvents(recurringEvent, LocalDate.now());
newEvents.forEach(EntityManager::persist);
}
public List&lt;RecurringEvent&gt; generateEvents(RecurringEvent recurringEvent, LocalDate currentDate) {
List&lt;Event&gt; generatedEvents = new ArrayList&lt;&gt;();
LocalDate nextDate = recurringEvent.startDate;
int generatedCount = 0;
LocalDate maximumDate = currentDate.plusDays(180);
while(generatedCount &lt; recurringEvent.maxNumber
&amp;&amp; !nextDate.isAfter(recurringEvent.endDate))
&amp;&amp; !nextDate.isAfter maximumDate) {
Event newEvent = createEvent(recurringEvent, nextDate);
generatedEvents.add(newEvent);
generatedCount++;
nextDate = Recurrence.nextDate(nextDate, recurringEvents.repeatPattern);
}
return generatedEvents;
}
}
</code></pre>
<p>Our test now becomes a simple unit test instead of a Spring/database type integration test.</p>
<pre><code class="language-java">class EventServiceTest {
@test
public void testGenerateEvents() {
RecurringEvent recurringEvent = RecurringEventFixture.create();
LocalDate today = LocalDate.of(2017, 1, 1);
List&lt;RecurringEvent&gt; generatedEvents = EventService.generateEvents(recurringEvent, today);
// assert the correct number of events were generated
Assert.assertEquals(4, generateEvents.size());
}
}
</code></pre>
<p>How many methods could you turn into simple pure function calls simply by removing side effect calls such as repositories from your business logic? Most projects I have worked on just sprinkle database calls throughout service layer.</p>
<p>In general the pattern I try to follow is;</p>
<ol>
<li>Retrieve all the data necessary to perform the business logic (side effect code).</li>
<li>Perform the business logic using in-memory data (pure functions).</li>
<li>Persist changes to the database (side effect code).</li>
</ol>
<p>These three steps can&rsquo;t be followed every time but when they can, the code will be much easier to test.</p>
</description>
</item>
<item>
<title>Akka Cluster Example</title>
<link>http://eweise.com/post/akka-cluster-example/</link>
<pubDate>Wed, 05 Feb 2014 22:00:06 -0800</pubDate>
<guid>http://eweise.com/post/akka-cluster-example/</guid>
<description>
<p>Akka provides powerful clustering capabilities but reading through the <a href='http://doc.akka.io/docs/akka/2.3.2/scala/cluster-usage.html'>online docs</a> it may not be obvious how to set it up for your particular needs. For my use case, I wanted to create multiple actors representing a particular service that would run on more that one machine. If one machine failed, actors on other machines could still handle the requests, as in a typical high availability scenario. In addition, I did not want the calling actor to have any knowledge of which machines could handle the request, only that one actor in the cluster would handle it. Akka&rsquo;s provides exactly these capabilities using the DistributedPubSubExtension provided in Akka&rsquo;s contrib module. This article will demonstrate how to create a clustered service using the DistributedPubSubExtension.
<p>In our example we have two types of actors; a BackendService actor which represents some arbitrary service running in the cluster and a WebService actor which handles http requests and in turn calls a BackendService to perform some work. The BackendService is stateless and there are many running in the cluster. The WebService just need to call any one BackendService to have the work performed.</p>
<h2 id="the-distributedpubsubmediator">The DistributedPubSubMediator</h2>
<p>Both the WebService and the BackEndService create a DistributedPubSubMediator. The DistributedPubSubMediator is an actor that maintains a registry of other ActorRefs and distributes them to peers around the network. This allows clients to refer actors by role instead of by a specific address. It also allows actors to join or leave the cluster and the mediator will track which actors are currently active.</p>
<pre><code class="language-scala"> val mediator = DistributedPubSubExtension(context.system).mediator
</code></pre>
<h2 id="backend-service-implementation">Backend Service Implementation</h2>
<p>In our BackendServiceActor constructor we simply need to create the mediator and register our actor with it. The mediator will in turn update all its peer mediators in the cluster informing them that our BackendServiceActor has joined the cluster</p>
<pre><code class="language-scala"> mediator ! Put(self)
</code></pre>
<p>The rest of the BackendActor is a standard Actor implementation where we listen for messages in a receive method</p>
<pre><code class="language-scala"> def receive = {
case PerformWork =&gt;
log.info(&quot;Backend Service is performing some work&quot;)
sender() ! OK
}
</code></pre>
<h2 id="the-web-service">The Web Service</h2>
<p>For our Web service we will use Spray.io which is a library built on top of Akka that provides all the functionality we need to process http requests. To use Spray we create an actor that inherits from the HttpService trait.</p>
<pre><code class="language-scala"> class WebServiceActor extends Actor with HttpService
</code></pre>
<p>Like in the BackendService actor, we need to create a mediator in the HttpService constructor. Then we can send messages to the BackendService via the mediator</p>
<pre><code class="language-scala">mediator ? Send(&quot;/user/backend-service&quot;, PerformWork, false)
</code></pre>
<p>As with all actors we need to implement a &lsquo;receive&rsquo; method. Our receive method will provide http routing information to Spray so it knows how to handle requests. Spray provides a DSL for doing this. In our example we implement just one route &ldquo;dowork&rdquo; in order to see our cluster in action</p>
<pre><code class="language-scala">def receive = runRoute {
path(&quot;dowork&quot;) {
onComplete(mediator ? Send(&quot;/user/backend-service&quot;, PerformWork, false)) {
case Success(value) =&gt; complete(&quot;OK&quot;)
case Failure(e) =&gt; complete(e.getMessage)
}
}
}
</code></pre>
<p>##Configuration
Both services need to provide configuration information to the ActorSystems. In the application.conf file we need to add the extension class</p>
<pre><code> extensions = [
&quot;akka.contrib.pattern.DistributedPubSubExtension&quot;
]
</code></pre>
<p>You can also configure the PubSubExtension with the following properties</p>
<pre><code>akka.contrib.cluster.pub-sub {
# Actor name of the mediator actor, /user/distributedPubSubMediator
name = distributedPubSubMediator
# Start the mediator on members tagged with this role.
# All members are used if undefined or empty.
role = &quot;&quot;
# How often the DistributedPubSubMediator should send out gossip information
gossip-interval = 1s
# Removed entries are pruned after this duration
removed-time-to-live = 120s
}
</code></pre>
<p>##Conclusion
A complete example that can run is available <a href="https://github.com/eweise/akka-pubsub-cluster-example">here</a>. Try booting up multiple BackendService instances. You will notice that the router selects actors randomly which is the default but can be changed to other strategies including round-robin. One issue I see in the example is that a delay occurs handling requests when a BackendService leaves the cluster, potentially causing the WebService requests to timeout for a brief time. This could possibly be hardened by adding retry logic into the Webservice. Overall Akka provides an incredible amount of functionality and the contrib module builds on top of the core features to making implementing clustered services almost trivial.</p>
</description>
</item>
</channel>
</rss>