Causes - a programming language feature concept
Tags: ruby, computerscience
Published: 2019-08-15 17:53:58

# Introduction
A typical programming language's standard library is often filled with methods which clearly complement each other.

A clear and common relationship is between an operation method, and a predicate method which becomes true if that operation has taken place. For example, immediately after calling the `clear` method on a Ruby `Array` instance, the `empty?` predicate becomes true. There are plenty of other ways to mutate an array such that it satisfies the `empty?` predicate, but `clear` is guaranteed to satisfy it every time with just one method call. To summarise: calling `clear` causes `empty?` to be true.

We've now established that there's a relationship between these methods. What if we could declare relationships like this to the language, and what advantages would this bring to both the programmer and the language itself?

# Proposing a language which includes causes
Let's take a look at a theoretical language which allows relationships like this to be declared. We'll call these relationships _causes_,  as calling one method _causes_ another predicate to become true.

I'll be writing Ruby-style pseudocode, as implementing complex functionality like this using Ruby's metaprogramming functionality seems feasible.

Here's how the `empty?` and `clear` methods could be declared in the `Array` class, with a cause given for `clear`:

```ruby
class Array
  # ...other definitions...

  def empty?
    length.zero?
  end

  causes { empty? } # <-- Here's our defined cause
  def clear
    each do |item|
      delete(item)
    end
  end
end
```

This code would associate the `empty?` and `clear` methods, specifying that an invocation of `clear` causes the predicate `empty?` to become true.

This could also be extended to more complex causes relying on the arguments passed to the method. For example, if an item has just been appended to an array (using `<<` in Ruby), then it must be the last item in that array.

A definition of this cause could look like:

```ruby
cause { |item| last == item } 
def <<(item)
  # ...array append logic...
end
```

Here, the predicate is more than a simple method call - the `last` method is invoked and has its result compared with the item to append.

# What is gained from doing this?
Thinking of a method's causes seems like yet another hurdle when writing code, so why bother?

## Effortless idempotency
Because it is specified exactly what will happen when they're called, simple methods with causes can be called idempotently. That is, if their effect has already happened, it shouldn't happen again.

Suppose you have an array of bytes called `bytes` supplied by a user, and you're about to process it in a way which requires a null terminator to be at the end. The user may or may not have included this null terminator. In traditional Ruby code, this could be done like so:

```ruby
bytes << 0 unless bytes.last == 0
```

Introducing an idempotent call syntax into our language, where we add a `?` prefix to the method's name, we could do this:

```ruby
bytes ?<< 0
```

This is possible because, by specifying what a method causes formally, we know exactly what `<<` will do and therefore can determine if it is required to call it. This particular code snippet would evaluate the predicate specified in the cause for `<<`; recall that this predicate is `|item| last == item`. If this predicate is false, i.e. the last item is not 0, then `<<` would be executed.

## Bringing declarative programming to a procedural language
If it is known what condition a method satisfies, then there is also the power to flip this information around. Given a condition we want to satisfy, we can find a method which causes it.

Suppose we have a variety of sorting algorithms defined on the language's array type, each with a cause specified such that the array satisfies a `sorted?` predicate after invoking them. To sort an array instance, we can write code that asks the language to satisfy the `sorted?` predicate, using a `satisfy` keyword:

```ruby
array = [4, 3, 7, 2]
satisfy array.sorted?
p array # => [2, 3, 4, 7]
```

The language is aware of which methods will satisfy the `sorted?` predicate and can select one accordingly, perhaps using an intelligent interpreter which tries each candidate method over time and establishes which method is faster for particular instances.

## Extra assertions for free
Each cause could optionally act as a postcondition assertion, where it runs after the associated method has been invoked. This is similar to the contract features built into Eiffel or D. For instance, in the `clear` and `empty?` example, the language will verify that the list really is empty when `clear` returns, and throw an assertion error if not. This could help catch bugs earlier.

## Easier-to-read code
Each cause can act as documentation, showing a programmer reading the code what implications a method will have. In addition, because causes can act as assertions, it is guaranteed that these implications are true.

# Conclusion
I think that implementing causes in a language could be a clean, unintrusive way to make code more expressive and easier to read. What do you think?