The mathematician Henri Poincare said:
Mathematics is the art of giving the same name to different things.
That is what I want to talk about today, about the names we attach to things. This matters when writing a computer program, using natural language or designing a graphical user interface.
Abstractions are what allows us to extract commonalities between many different concrete things. This allows us to simplify and enhance our expressive power. When programming this means shorter code and less boilerplate.
I was inspired to write about this, following two recent experiences. The first was getting deeper into the Julia programming language and realizing how this idea of naming different things the same, is so fundamental to the power of the language.
The second most recent experience was trying to learn the natural language for humans to speak and write called Toki Pona. It is a language with only 120 words.
For a software developer who has ever marveled at the simplicity and power of the LISP programming language, there is a recognizable quality to Toki Pona, but as a spoken language. Just like LISP, Toki Pona attempts to achieve great flexibility through the usage of some well thought out basic building blocks. The power comes from the flexibility in which these blocks can be combined.
Mathematics and Abstract Language Concepts
In very ancient times, there was no words for abstract numbers. If you wanted to say, “two oxen” or “two sheep” then that would be two different words entirely. It would not be possible to tease out a part that meant just the number two. Numbers in other words were concrete. You could not speak of a number of items without also stating exactly what sort of items you were talking about.
Somehow, some smart guy figured out that there was a shared property among items of particular numbers which could be extracted. That e.g. three stones, three sheep and three oxen, while different all share the common property of the number 3, and that we could create ways of representing this number.
This allows us to write how to add up or subtract things without repeating that description for every possible type of item we might want to add up, multiply or whatever.
Abstractions are also what allows us to write addition, subtraction, multiplication and division with the same symbols regardless of the type of numbers we operate on. Dealing with decimal numbers or fractions e.g. is slightly different than dealing with natural numbers.
Abstractions in a Programming Language
This is even more obvious inside a microprocessor (CPU). The CPU will have different machine code instructions for adding integer numbers and floating point numbers.
Even at different bit lengths (number of digits) there will be different CPU instructions. But as programmers we don’t want to write different operators for different number types.
Since that would require us to implement the same algorithm multiple times for different bit sizes and whether we were dealing with floating point or integer numbers. In other words dealing with concrete cases quickly becomes tedious and requires lots of code repetition.
That is essentially what a programming language is about, letting the programmer describe a solution to a problem in an abstract manner and then translate this to machine code dealing with very concrete concepts.
Julia supports multiple dispatch, which allows us to describe the same operator/function with different argument types.
+(a::Int8, b::Int8) = add_int8(a, b)
+(a::Int16, b::Int16) = add_int16(a, b)
+(a::Float32, b::Float32) = add_float32(a, b)
(operators are functions in Julia, hence we can name the function
Defining an abstract plus operator rather than dealing with concrete plus operators/functions such as
add_float32, allows us to express other algorithms abstractly.
E.g. imagine Julia didn’t know how to multiply. We could describe it like this for any integer number:
function *(a::Integer, b::Integer)
sum = 0
for i in 1:a
sum += b
This saves us from defining multiply for every integer bit size.
Geometric Shapes Example
Let take a higher level example. We might want to write an algorithm which deals with intersections between different geometric shapes. You don’t want to write the algorithm for every concrete geometric shape which might intersect.
Rather you want the function
intersect to be generic and operating on the abstract type
Shape, rather than a concrete shape such as a
function intersect(a::Circle, b::Rectangle)
function intersect(a::Polygon, b::Circle)
function intersect(a::Polygon, b::Rectangle)
To define these concrete shapes as subtypes of an abstract shape type, we first have to state that
Shape is an abstract type:
abstract type Shape
Then we define each type as being a subtype of
struct Rectangle <: Shape
struct Circle <: Shape
struct Polygon <: Shape
(I am not providing the full implementation details here, as that isn’t relevant for the topic.)
Now we could define some algorithm
foobar doing stuff on some shapes.
function foobar(a::Shape, b::Shape)
if intersect(a, b)
The exact implementation is not important. What matters is that the code inside the
foobar function does not have to care about the concrete type the shapes
Abstractions in a Natural Language
This brings us to what I think is an interesting point, because we rarely thing that much about it. How do we create abstractions in a natural language so we can keep it small and avoid verboseness?
“ma telo,” means “swamp” in the language Toki Pona. If we look at the word “swamp”, there is really no way of guessing what it means. It does not consist of smaller parts which we may use to decipher the meaning. We simply need to have learned through a description what swamp means.
“ma telo,” on the other hand may be interpreted by anyone who know the 120 root words of Toki Pona.
Just like the
intersect or plus operator in the Julia programming language, words in Toki Pona takes on a large number of possible meanings. Thus each word is highly abstract. An abstract concept may be viewed as the union of many concrete related concepts.
Thus what Toki Pona is all about is intersecting the meanings of abstract words to derive more concrete concepts. Words in Toki Pona means both nouns, verbs, adjectives etc. There are no distinct verbs or adjectives. Words are far broader in meaning.
So for instance the root word ma could mean land, earth, soil, country etc. We could say that a swamp is a form of land. But if we want to be specific when referring to this land we can’t just say ma, as that is too generic.
However telo means anything from wet, water, liquid, drinking, body of water, stream, milk, sea, to area covered with water etc.
Thus by combining ma and telo we indicate that we are dealing with some sort of land area which is wet or covered with water.