Reasoning by Analogy is a powerful learning, problem-solving, and debugging technique. It’s a four-step process:
- You have a question about something that you don’t understand
- Translate the question to something similar that you do understand (called the analogue)
- Solve the problem for this thing you understand
- Translate the solution back to the original situation
While you may not think about this explicitly, you use this kind of reasoning all the time when you leverage past experience to solve new problems.
Grade school math
Alice has 512 apples and Bob has 256 apples. How many do they have all together?
10-year-old you hates these kinds of problems. First of all, why do Alice and Bob have so many apples??? 🍎🍏🍎🍏🍎
More importantly, what does the teacher want you to do here? You know it’s math but what operation do you need to do? Those big numbers are just making things worse.
Time to simplify.
Alice has 2 apples and Bob has 1 apple. How many do they have all together?
Intuitively, you count up on your fingers. Together Alice and Bob have 3 apples. What did you just do? Oh, it’s an addition problem!
Back to the original problem, you add the two big numbers together to get 768 apples. You’re going to get a good grade on this one.
10-year-old you doesn’t know it but you just used an analogy to reason your way out of a problem that had you stuck.
Parameterized Html and List types
Grown-up you is now a web developer. You’ve been picking up this new Elm
language. You’re confused by this parameterized Html
type. In particular,
you’re trying to write a generic signature for this function.
type Msg = Increment | Decrement
someHtml : Html a
someHtml =
div [onClick Increment] []
The compiler is having none of it and is giving you errors about your type being
too generic. It thinks the type should be Html Msg
. How can a type be too
generic?!
It’s hard to get the proper intuitions about these parameterized types. Wait,
that’s not quite true. You have a good feel for the List
type which is
parameterized. Can you use your knowledge of List
to understand Html
? Time
to construct an experiment!
someList : List a
someList =
[Increment]
Aha! Same error about the type being too generic. That sort of makes sense
because you can see it is a hard-coded list of messages. If that’s true then
what’s the point of List a
? A hardcoded [1,2,3]
would have to be List Int
, and a hardcoded ["hello", "world"]
would have to be List String
.
Wait a minute, what about the empty list?
emptyList : List a
eptyList =
[]
That does compile!
Again on further reflection that makes sense. The empty list is a valid List String
and also a valid List Int
, and also a valid List Msg
. That would
mean that any function someList : List a
can only be an empty list. Taking
this idea further, you wonder if having a more generic signature actually limits
the valid implementations. Could this idea also apply to your original HTML
problem?
Given that Html Msg
means “some HTML with event handlers that emit Msg
values”, what would be the equivalent of an empty list? Perhaps some HTML with
no event handlers?
staticHtml : Html a
staticHtml =
div [] []
Oh look that compiles! It looks like you were right. Just like
someList : List a
must be empty, it looks like someHtml : Html a
must be
static!
You used your intuition about lists in combination with reasoning by analogy to learn something new. Time to celebrate! 🎉
False analogies
Reasoning by analogy is a powerful learning and debugging technique but it has one big weakness - false analogies.
It’s important to be really careful in step 2 where you find a similar problem that you already understand. If your analogue is not really similar, then your solution won’t translate back correctly. This can lead you to wrong conclusions!
Looking back at that grade-school math problem, what if you’d “simplified” the problem to
Alice has 2 apples and Bob has 1 apple. How many more does Alice have?
The solution here (subtraction) will not solve the original problem because our simplified problem is no longer analogous to original.
If you’re using reasoning by analogy as a debugging tool, make sure you can reproduce the same bug with your analogue problem.
More than just learning
Reasoning by analogy is a great tool for learning but it can apply to a lot of other situations too. I commonly use it for debugging, such as this random generators stack overflow bug, where I converted the problem to a more tractable function composition problem, figured out the problem there, and then backported the solution to the original random generator problem.
It’s also really helpful as a general problem solving tool. When I get stuck writing complex JSON decoders, I will instead convert the problem to a more intuitive Maybe problem, solve it there, and then backport the solution to Json.Decode
.
Other types of reasoning
Reasoning by analogy has its roots in classical philosophy. There are some other classical reasoning techniques including:
- Deductive reasoning (big picture -> details)
- Inductive reasoning (details -> big picture)
- Process of elimination
I have a whole article dedicated to using these classical reasoning techniques for debugging, including a section on reasoning by analogy.