Logic


Here is a very basic example in prolog written by Sir Bevedere from Monty Python and the Holy Grail:

witch(X)  :- burns(X), female(X).
burns(X)  :- wooden(X).
wooden(X) :- floats(X).
floats(X) :- sameweight(duck, X).

female(girl).
sameweight(duck, girl).

?- witch(girl).

In prolog there are rules of the form:

Head :- Body

The following rule says, roughly, that X is a witch if X burns and X is a female.

witch(X) :- burns(X) and female(X).

A rule witout a body is a fact. For example, this fact says that girl is female:

female(girl).

Prolog is not fashionable. Erlang was originally based on Prolog and whilst it is fashionable, it is not fashionable for any similarity to prolog.

Notice though that Prolog is declarative. It provides a clear way to express relations without you, the programmer, having to mess around with plumbing, telling the computer what to do.

So what?

Well, to really understand what this is worth you should try implementing a declarative language. For example, here is a rough implementation of just the minimum to allow only the rules shown above to be expressed in a similar fashion to how they are in Prolog.

Array::deepEqual = (compare) ->
  return false unless @length is compare.length
  for i in [0...@length]
    if @[i] instanceof Array and compare[i] instanceof Array
      return false unless @[i].deepEqual compare[i]
    else
      return false if @[i] isnt compare[i]
  true

rule = (name, args...) ->
  @all ?= []
  if typeof args[0] is 'function'
    @[name] = args[0]
  else
    resolver = (args...) ->
      for facts in @all[name]
        if facts.deepEqual args
          return true
      false
    @[name] = resolver unless @[name]?
    @all[name] ?= []

    @all[name].push args

Q = (question) ->
  try
    question()
  catch e
    false

Here’s how to use it:

rule 'witch', (X) -> burns(X) and female(X)
rule 'burns', (X) -> wooden X
rule 'wooden', (X) -> floats X
rule 'floats', (X) -> same_weight 'duck', X

rule 'female', 'girl'
rule 'same_weight', 'duck', 'girl'

Q? -> witch 'girl'

That last line evaluates to true.

The plumbing, even for this extremely simple example is quite horrendous. It also takes longer to implement than you expect. Perhaps there are much more elegant ways to write this in CoffeeScript. That’s not the point.

What’s the point?

Try to avoid falling quickly for seduction of writing a familiar imperative (give the computer individual steps) solution when there’s an existing declarative solution. This applies everywhere, from manually writing for (;;) loops, to stateful code, to managing environments.

Often times a declarative solution requires that you learn a new technique, language or way of thinking about something before you can get very far. However, having to learn before you get very far is better than having to learn after you get very far. Especially when you do get very far, and discover your solution is rubbish.



CoffeeScript in Action


CoffeeScript in Action book cover

I'm the author. Get it from Manning.