Good programmers try to eliminate similarities as much as the programming language allows.
from the beginning of III - Abstraction
recipe for creating abstractions
III - 14 Similarities Everywhere
14.1 Similarities in Functions
This chapter focuses on identifying similarities in code, even in code that is solving two seemingly different problems.
; Los -> Boolean
; does l contain "dog"
(define (contains-dog? l)
(cond
[(empty? l) #false]
[else
(or
(string=? (first l)
"dog")
(contains-dog?
(rest l)))]))
; Los -> Boolean
; does l contain "cat"
(define (contains-cat? l)
(cond
[(empty? l) #false]
[else
(or
(string=? (first l)
"cat")
(contains-cat?
(rest l)))]))
In this case, both functions have the same structure, although they're trying to match different strings. This can be abstracted to a function that looks for a string s
in a Los
(list of strings). Now, given
; String Los -> Boolean
; determines whether l contains the string s
(define (contains? s l)
[...]
the following can be produced:
; Los -> Boolean
; does l contain "dog"
(define (contains-dog? l)
(contains? "dog" l))
; Los -> Boolean
; does l contain "cat"
(define (contains-cat? l)
(contains? "cat" l))
14.2 Different Similarities
In this case, a function that applies a function to data is abstracted.
; Lon Number -> Lon
; select those numbers on l
; that are below t
(define (small l t)
Given another function that selected numbers above t
, one notices another possible abstraction.
(define (extract R l t)
where R
is a function defining a relation (like <
, !=
, etc.).
14.3 Similarities in Data Definitions
But not only are abstractions possible in the bodies of functions, they can be in the data definitions too. Given
; A Lon (List-of-numbers)
; is one of:
; – '()
; – (cons Number Lon)
and an analogous function for Los (List-of-strings)
,
; A [List-of ITEM] is one of:
; – '()
; – (cons ITEM [List-of ITEM])
III - 15 Designing Abstractions
15.1 Abstractions from examples
Two seemingly different functions are, upon closer inspection, just applying a function to all its elements. The chapter concludes with a derivation of the map
function.
A recipe for creating abstractions is introduced — which I presently recognize as also a pattern for refactoring.
15.2 Similarities in Signatures
Now, we look at function signatures as candidates for abstraction, too.
Both signatures and data definitions specify a class of data; the difference is that data definitions also name the class of data while signatures don’t. Nevertheless, when you write down
; Number Boolean -> String (define (f n b) "hello world")
your first line describes an entire class of data, and your second one states that
f
belongs to that class.
Thus,
can be abstracted to
; [X Y] [List-of X] -> [List-of Y]
with X
and Y
as parameters. Going even further,
; [X Y] [List-of X] [X -> Y] -> [List-of Y]
where [X -> Y]
introduces a function transforming X
in Y
: this is the map
function, again.
15.3 Single Point of Control
Form an abstraction instead of copying and modifying any code.
At this point, the book formalises something that I had already unearthed from other programming practices: having a single point of control can be good, because a change in one place will trigger the changes everywhere. This simplifies the process immensely, even if the abstracted code is/might be a bit trickier to assess at a first glance.
III - 16 Using Abstractions
16.6 Designing with Abstractions
A lot of work with local
. After a while – specially in the design recipe for abstraction – I realised the use of local
is very similar to using lambda
anonymous functions in map
, filter
, and other functional tools in Python. That is introduced in the following chapter, III - 17 Nameless Functions.
III - 17 Nameless Functions
There's a very funny reference to Fermat, and a link to Little Schemer that looks great and very interesting.