PurelyFunctional.tv Newsletter 445: 2 Kinds of genericity?

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.

Issue 445 - October 07, 2021 · Archives · Subscribe

Design Idea 💡

2 Kinds of genericity?

In my book, Grokking Simplicity, I show that when you draw the call graph of your functions, the further down the call graph from main(), the more generic your functions are. Conversely, the most specific function is main(). However, people also use generic (or general) to talk about polymorphic functions. This other use throws a monkey wrench into my simple scheme. I'll explore that monkey wrench today.

The basic principle that my scheme is based on is easy to state: We build specific solutions out of generic constructs. Generic and specific are relative terms, two poles on a spectrum of their applicability. The tangent function is applicable in many domains, and hence generic. A function to calculate the velocity of a satellite in a specific orbit around Io (the moon of Jupiter) is very specific. We can imagine that the orbit function uses tangent, but not vice versa. There is a natural alignment between generality and the call graph. And because of this, I've preferred using the terms generic and specific rather than more or less abstract (see this exploration).

But is genericity quite so simple? A kind reader pointed out that we also use the term generic to refer to polymorphic functions. A polymorphic function accepts values of different types and dispatches to the appropriate implementation for each type. It is "generic" (or "general") in its argument and calls more specific functions. It appears that polymorphism locally reverses the alignment between genericity and the call graph.

I don't know what to make of this. Does it destroy the model? Is there some way to fit polymorphic functions into the scheme that I can't see yet? Does the reversal of alignment explain the power of polymorphism (much like how Inversion of Control reverses the dependency arrows)? Or maybe the term "general" is overloaded, and we're talking about two different definitions.

I would appreciate any comments or references to prior work on this as I explore how polymorphism fits.

Pandemic update 😷

I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.

I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.

Stay healthy. Wash your hands. Wear a mask. Get vaccinated if you can. Take care of loved ones.

Clojure Challenge 🤔

Last issue's challenge

Issue 444

This week's challenge

Dueling Knights

A chess board has nothing but knights ♞ and empty spaces. In this setup, the color of the knights doesn't matter. Each knight could potentially capture any other. Your job is to write a function to figure out which knights can capture each other, if any.

A board is represented as a vector of vectors, each 8 elements long. A 0 represents an empty square. A 1 represents a knight. Your function should return a collection of pairs. The pairs represent the positions of the two knights that can capture each other.

For reference, see this site for understanding how knights move and capture.

Examples

(captures [[0 0 0 1 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 1 0 0 0 1 0 0]
           [0 0 0 0 1 0 1 0]
           [0 1 0 0 0 1 0 0]
           [0 0 0 0 0 0 0 0]
           [0 1 0 0 0 0 0 1]
           [0 0 0 0 1 0 0 0]]) ;=> [] ;; no captures

(captures [[0 0 0 1 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 1 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]]) ;=> [#{[0 3] [2 4]}] ;; one capture

(captures [[0 0 0 1 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 1 0 0 0]
           [0 0 1 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]
           [0 0 0 0 0 0 0 0]]) ;=> [#{[0 3] [2 4]} #{[2 4] [3 2]}] ;; two
capture

Thanks to this site for the problem idea, where it is rated Very Hard in PHP. The problem has been modified.

Please submit your solutions as comments on this gist.

Rock on!
Eric Normand