Skip to content

Commit 00001fd

Browse files
committed
Add a sample chapter
1 parent 02f0230 commit 00001fd

File tree

6 files changed

+248
-0
lines changed

6 files changed

+248
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
title: Sample Chapter 7 - Calculations and Actions
3+
layout: sample
4+
---
5+
6+
## Functions
7+
8+
As an industry, we have invented a lot of phrases to describe callable subprograms within a larger program.
9+
We have the very generic _subroutine_.
10+
Some languages (notably Pascal) distinguish between _functions_ that return a result, and _procedures_, which don't; but most developers use the terms interchangeably.
11+
Then there are _methods_, which are subroutines associated with an object (or a class, in the case of static methods).
12+
13+
The C language calls them all functions but has a special `void` type to represent the absence of a return value.
14+
This was carried forward into Java.
15+
Kotlin uses `Unit` in almost the same way, except that `Unit` is not the absence of a return value, but rather a singleton value that is returned instead.
16+
17+
In this book we use the term _function_ to refer to both result-returning and non-result-returning subroutines, whether freestanding or associated with an object.
18+
Where it's significant that they are associated with an object, we'll call them methods.
19+
20+
Whatever we call them, functions are one of the fundamental building blocks of our software.
21+
We define them with some sort of notation, generally the programming language we are using.
22+
They are also generally fixed during a run of the program; in static languages, at least, we don't usually redefine functions on the fly.
23+
24+
This is in contrast to the other fundamental building block: data.
25+
We expect data to vary as we run our program, and different data is bound to variables.
26+
Variables are called variables because they are, wait for it, variable.
27+
Even when they are `final`, or `val`, they are usually bound to different data in different invocations of a function.
28+
29+
We hinted earlier at a subdivision of functions into those that return a result and those that do not.
30+
This might seem like a fundamental difference, but in practice there is a more useful way to divide functions: into _calculations_ and _actions_.
31+
32+
Actions are functions that depend on when or how many times they are run; calculations are functions that don't—they are timeless.
33+
Most functions that we write are actions, because we have to take special care to write code that doesn't depend on when it is run.
34+
How would we go about doing that?
35+
36+
## Calculations
37+
38+
To be a calculation, a function must always return the same result given the same inputs.
39+
The inputs to a function are its parameters, which are bound to arguments when the function is called.
40+
So a calculation always returns the same result when called with the same arguments.
41+
42+
Take a `fullName` function:
43+
44+
```kotlin
45+
fun fullName(customer: Customer) = "${customer.givenName} ${customer.familyName}"
46+
```
47+
48+
`fullName` is a calculation: it will always return the same value when supplied the same `Customer`.
49+
This is true only if `Customer` is immutable, or at least `givenName` and `familyName` cannot change.
50+
To keep things simple, we'll say that calculations can only have parameters that are values, as defined in [Chapter 5](https://java-to-kotlin.dev/).
51+
52+
Methods, and the disguised methods that are member properties, can also be calculations:
53+
54+
```kotlin
55+
data class Customer(
56+
val givenName: String,
57+
val familyName: String
58+
) {
59+
fun upperCaseGivenName() = givenName.toUpperCase()
60+
61+
val fullName get() = "$givenName $familyName"
62+
}
63+
```
64+
65+
For a method or extension, the receiver `this`, and any property accessed via `this`, is also an input.
66+
So both `upperCaseGivenName` and `fullName` are calculations because `givenName` and `familyName` are both values.
67+
68+
An extension function or property can also be a calculation if the data it depends on is a value:
69+
70+
```kotlin
71+
fun Customer.fullName() = "$givenName $familyName"
72+
73+
val Customer.fullName get() = "$givenName $familyName"
74+
```
75+
76+
****
77+
### Sidebar - Computed Property or Function?
78+
You may have wondered when to define a computed property and when to have a function that returns a result.
79+
Computed properties are confusing if they return different results at different times, at least when defined on value types (and you'll be realizing by now that your authors think that most of our types should be value types).
80+
So a good rule of thumb is to reserve computed properties for calculations.
81+
82+
We expand on this topic in [Chapter 11](https://java-to-kotlin.dev/).
83+
84+
****
85+
86+
The result of a calculation may depend on data that is not passed as parameters, but only if that data does not change.
87+
Otherwise, the function's result would be different before and after the change, which would make it an action.
88+
Even if a function always returns the same result for the same parameters, it may still be an action if it mutates something (either a parameter or an external resource such as a global variable or a database).
89+
For example:
90+
91+
```kotlin
92+
println("hello")
93+
```
94+
95+
`println` always returns the same `Unit` result given the same `hello` input, but it is not a calculation.
96+
It is an action.
97+
98+
## Actions
99+
100+
`println` is an action because it _does_ depend on when and how many times it is run.
101+
If we don't call it, nothing is output, which is different from calling it once, which is different from calling it twice.
102+
The order that we call `println` with different arguments also matters to the results we see on the console.
103+
104+
We call `println` for its _side effect_—the effect it has on its environment.
105+
Side effect is a bit of a misleading term because, unlike drug side effects, they are often exactly the thing that we want to happen.
106+
Maybe _outside effect_ would be a better name, to emphasize that they are external to a function's parameters, local variables, and return value.
107+
In any case, functions with observable side effects are actions not calculations.
108+
Functions returning `void` or `Unit` are almost always actions, because if they do anything, they have to do it by side effect.
109+
110+
As we saw previously, code that reads from external mutable state must also be an action (provided that anything does actually mutate the state).
111+
112+
Let's look at a `Customers` service:
113+
114+
```kotlin
115+
class Customers {
116+
fun save(data: CustomerData): Customer {
117+
...
118+
}
119+
fun find(id: String): Customer? {
120+
...
121+
}
122+
}
123+
```
124+
125+
Both `save` and `find` are actions; `save` creates a new customer record in our database and returns it.
126+
This is an action because the state of our database depends on when we call it.
127+
The result of `find` is also time sensitive, because it depends on previous calls to `save`.
128+
129+
Functions that have no parameters (this doesn't include methods or extension functions, which can have implicit parameters accessed via `this`) must either be returning a constant or be reading from some other source and so be categorized as actions.
130+
Without looking at its source, we can deduce that a top-level function `requestRate` is almost certainly an action, reading from some global mutable state:
131+
132+
```kotlin
133+
fun requestRate(): Double {
134+
...
135+
}
136+
```
137+
138+
If a function with the same apparent signature is defined as a method, it is probably a calculation that depends on properties of `Metrics` (provided `Metrics` is immutable):
139+
140+
```kotlin
141+
class Metrics(
142+
...
143+
) {
144+
145+
fun requestRate(): Double {
146+
...
147+
}
148+
}
149+
```
150+
151+
We say _probably_ because in languages like Java or Kotlin that allow input, output, or accessing global mutable data from any code, there is no way to be sure whether a function represents a calculation or action short of examining it and all the functions that it calls.
152+
We'll return to that problem soon.
153+
154+
## Why Should We Care?
155+
156+
We should obviously pay special attention to some actions in our software.
157+
Sending the same email to every user twice is a bug, as is not sending it at all.
158+
We care exactly how many times it is sent.
159+
We may even care that it is sent at exactly 8:07 a.m., so that our offer for a free first-class upgrade is at the top of our customer's inbox when they read their email over breakfast.
160+
161+
Other seemingly innocuous actions may be more nocuous than we think.
162+
Changing the order of read and write actions causes concurrency bugs.
163+
Error handling is much more complicated if the second of two sequential actions fails after the first succeeded.
164+
Actions prevent us from having free rein to refactor our code, because doing so may change when or whether they are invoked.
165+
166+
Calculations, on the other hand, can be invoked at any time, with no consequences for calling them again and again with the same arguments except a waste of time and energy.
167+
If we are refactoring code and find that we don't need the result of a calculation, we can safely not invoke it.
168+
If it is an expensive calculation, we can safely cache its result; if it is inexpensive, we can safely recalculate it on demand if that simplifies things.
169+
It is this feeling of safety that puts the smug smile on the faces of functional programmers (well, that and knowing that a monad is just a monoid in the category of endofunctors).
170+
Those functional programmers also have a term for the property of a function that makes it a calculation: _referential transparency_.
171+
If a function is referentially transparent, we can replace its call with its result, and we can only do that if it doesn't matter when or if we call it.
172+
173+
****
174+
### Sidebar - Procedural Code
175+
176+
Nat and Duncan are both old enough to have learned to program in Sinclair BASIC on the ZX81.
177+
This dialect had no immutable data, and no support for subroutines, parameters, or local variables.
178+
It requires real discipline to program in such a system, because practically every line of code is an action and so potentially affects the functioning of every other statement.
179+
180+
This is in fact very close to the way that our computers actually work, with mutable values held in registers and global memory, manipulated by machine-code actions.
181+
The evolution of programming languages has been a process of restricting the ultimate flexibility of this model, so that humans can better reason with the code that they create.
182+
183+
****
184+
185+
## Why Prefer Calculations?
186+
187+
We like calculations because they are so much easier to work with, but ultimately our software needs to have an effect on the world, which is an action.
188+
There is no overlap though; code can't be an action and a calculation, both timeless and time-dependent.
189+
If we take some code that is a calculation and have it invoke an action, then it becomes an action, because it will now depend on when or whether it is called.
190+
We can think of calculations as the purer code, where code inherits the most tainted level of all of its dependencies.
191+
We see the same thing with susceptibility to errors in [Chapter 19](https://java-to-kotlin.dev/).
192+
If we value purity (which in all these cases brings ease of reasoning and refactoring), we must strive to pull the boundary between impure and pure code to the outer layers of our system—those closest to the entry points.
193+
If we succeed, then a significant proportion of our code can be calculations and, hence, easily tested, reasoned with, and refactored.
194+
195+
What if we don't succeed in keeping actions at the bottom of our call stack?
196+
Then we can fix things with refactoring!

_config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ collections:
3535
journeys:
3636
output: true
3737
layout: default
38+
chapter_samples:
39+
output: true
40+
layout: default

_includes/nav.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<h3>Resources</h3>
99
<ul>
1010
<li><a href="{% link reviews.md %}">Reviews</a></li>
11+
<li><a href="{% link sample-chapters.html %}">Sample Chapters</a></li>
1112
<li><a href="{% link code.md %}">Example code</a></li>
1213
<li><a href="https://www.youtube.com/c/DuncanMcGregors">YouTube videos</a></li>
1314
<li><a href="https://www.oreilly.com/catalog/errata.csp?isbn=0636920453178">Errata</a></li>

_layouts/sample.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
layout: default
3+
---
4+
<h1>{{ page.title }}</h1>
5+
6+
<p>
7+
This is the introduction to a chapter of the book, presented without the refactoring content.
8+
</p>
9+
10+
{{content}}
11+
12+
<h2>Read More</h2>
13+
<p>To read a detailed refactoring example, please <a href="/">buy the book</a>!</p>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
layout: post
3+
title: Sample Chapter 7 - Actions to Calculations
4+
tags:
5+
---
6+
7+
We have found [Eric Normand](https://ericnormand.me/)'s division of code into Data, Calculation and Actions very useful.
8+
He introduces the concept in his excellent [podcast](https://ericnormand.me/podcast) and book [Grokking Simplicity](https://grokkingsimplicity.com/).
9+
10+
Unfortunately he doesn't (yet) seem to have written about the subject on the web.
11+
To fill this gap, we've published the introduction to [Chapter 7 - Actions to Calculations](/chapter_samples/chapter07-calculations-to-actions.html),
12+
which is our interpretation of at least part of the concept.
13+
14+
15+

sample-chapters.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: Samples
3+
layout: default
4+
---
5+
<h1>{{page.title}}</h1>
6+
7+
<p>
8+
Most chapters in the book have some discussion about how we might do things in Java, then how to achieve the
9+
same in Kotlin, followed by an example of how to refactor to the Kotlin way of doing things.
10+
</p>
11+
12+
<p>
13+
Here we present some of the discussions. If you want the refactorings, then please <a href="/">buy the book</a>!
14+
</p>
15+
16+
<ul>
17+
{% for j in site.chapter_samples %}
18+
<li><a href="{{j.url}}">{{j.title}}</a></li>
19+
{% endfor %}
20+
</ul>

0 commit comments

Comments
 (0)