Skip to content

Instantly share code, notes, and snippets.

@dkinzer
Last active November 3, 2017 01:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dkinzer/5dd183787773029156d8 to your computer and use it in GitHub Desktop.
Save dkinzer/5dd183787773029156d8 to your computer and use it in GitHub Desktop.

Predicate Conundrum.

In an article entitled "Boolean Externalities" developer Avdi Grimm describes a difficult debugging experience that resulted from an erroneous boolean return from deep within a series of chained predicate functions summarized in the following sketch:

class TypeA
# ...
  def question1?(instance_typeb)
    question2? || instance_typeb.question3?
  end
# ...
end

class TypeB
  # ...
  def question3?
    question4? && intance_typec.question5?
  end

  # ...
  def question4?
    question6? && question7?
  end

  # ...
  def question7?
    instance_typed.question8?
  end
  # ...
end

class TypeD
  # ...
  def question8?
    # ...
  end
  # ...
end

After an arduous debugging process which he describes as going "Down the rabbit hole" Avdi finally finds the offending predicate function deep within a stack of questions (question8); he fixes the problem but he finds himself perplexed as to what he could have done differently to avoid the situation given that he had followed many modern development techniques.

Avdi's use of the "rabbit hole" metaphor is quite apt for this example because as he describes the process of traveling deeper and deeper into the chain of predicate functions there is quite a visceral sense of decent.

Maybe the problem here is that this whole idea of the predicate function is wrong; No, not entirely, of course, just some subset of these that do not take arguments. I think about some of the core predicate operators, for instance ==, >, <, etc. What these operators almost all have in common is that they operate on a thing or things; that is to say, they are used to ask a question about something substantial and immediate. And, in most cases, if the need arises to find out why a specific predicate is true or false, the answer is pretty obvious by looking at the arguments (3 > 1), (2 == 4)

In functional programming there is this concept of the pure function that essentially equates a function to a map with a 1 to 1 correlation between arguments and results (i.e. the function always returns the same exact thing given the same input). Well, there's more to it than that but the important thing is that if a function is given no argument, then the answer should always be the same for no arguments.

Are there predicate operators that take no arguments? The only example I can think of for a predicate operator that does not take an argument is "else", since else will always return :true (I'm mixing apples and oranges here because operators aren't functions but for the sake of this argument I think it's OK to think of them in the same way).

In Avdi's example, these predicate functions aren't being passed anything. Therefore, functionally speaking they should be returning the same thing. But one may argue that indeed "something" is being passed; because we're talking objects here, we can think that the implicit argument "passed" to these predicates is always the object it self.

OK then, so we have a way to inspect the object and get the answer? There is the crux. These predicates aren't asking questions of self they are asking things about some other self. And when these things get chained together then it's hard to know what thing it was that was being asked a question about.

I haven't really answered how to solve this in a better way; and sorry for being so hand wavy about my attempt at explaining some of what I think is important to consider. Clearly this is a hard question.

However, I believe one possible solution is to limit these predicate functions such that the thing a question is being asked about is more immediate as is the case for the core predicate operators. But then I'm not certain about the details of how that would be applied to the problem at hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment