Programs and Intent

Programs and Intent

9 Nov 2021
Programming, Verification

What does this program do? At the most reduced level, one could say that a program’s behavior is defined by the effect it has on the hardware running it. That’s not very useful however; when we’re programming, we often have to deal with legacy code and tease out the original intent of the code.

Saying that the meaning of a program is entirely encapsulated by the code is saying that the intent and the implementation are the same. They so rarely are!

Today I found some Elixir code that looked like the following:

@spec all_have_key?(lst :: [%{}], needed_key :: String.t()) :: boolean()
def all_have_key?(lst, needed_key) do
  lst
  |> Enum.map(fn m -> m |> Map.keys() |> Enum.any?(fn k -> k == needed_key end) end)
  |> Enum.all?()
end

(Note, I just threw that together—possible syntax errors in there.)

What is this code trying to do? It’s trying to check that each map in a list has a given key and return true or false on that condition. I don’t think the code even had a @spec to help explain that: all I had was the function name (which was not as clear as all_have_key) and the source.

After a few moments of reflection, I rewrote it to this:

@spec all_have_key?(lst :: [%{}], needed_key :: String.t()) :: boolean()
def all_have_key?(lst, needed_key) do
  lst
  |> Enum.map(&Map.has_key?(&1, needed_key))
  |> Enum.all?()
end

That big long complicated bit has a built-in function. The built-in function is more efficient because it doesn’t traverse the entire list of keys searching for a match: with a map, you get O(1) lookup time.

So what does a program mean? I’m pretty confident that I preserved the intended meaning of this program. But what’s a better way to express that intent?

Tests are useful, but they don’t capture everything. While I think this function was tested, no test can ever ensure 100% preservation of intent. Tests can only find witnesses of meaning mismatches.

Type systems are helpful too. But types come in varying degrees of precision: some languages give you an Int, while others give you Int or Nat or ∈ {1, 2, 3}. More powerful type systems let you express more of your intent in a way that can be mechanically checked, but they tend to also be more burdensome.

This is an open question that I know there’s a lot of ongoing research around. I’m excited to see what I find!

Mastodon