TRANSCODE Explorations into the Code Transcendental.

Hardly Functional

Is Thinking Functionally a Black Art?

As I explained in a previous post, I have been writing a genetic algorithm to find the best layout of a keyboard based on a set of ergonomic principles. I decided to write the program in both Go and Elixir as a way to “learn me some new programmings languages for the greater good”. Writing the Go program went very smoothly. I had a good working program in just over a day’s work. The Elixir code on the other hand has taken me close to three days and I am still trying to work out issues. I attribute most of this long spell to the paradigm shift one must make when switching from a procedural to a functional way of doing things.

I have two examples from my work that I think demonstrate the difficultly of this shift.

Originally I had written the following code:

    def evolve(gen) do
      :random.seed

      best = 0
      size = 16

      pop = random_keyboards(size)

      Enum.reduce (1 .. gen), pop, fn(i, p) ->
        newpop = List.concat(breed(p), p)
        newpop = natural_selection(newpop, size)

        first = Enum.at(childs, 0)
        display(first)

        if best < first.score do
          best = first.score
          size = 16
        else
          size = size + 2
        end         

        newpop
      end
    end

It all seemed rather reasonable to me. Turns out however, Elixir thought otherwise:

variable best is unused
variable size is unused

My procedural mindset was geared toward the idea of using variables to track a state while iterating around in a loop. But for a functional language this is not the correct way to think about the problem. By reassigning best and size within the reduce block function I was in effect creating new variables, not reassigning the ones defined outside.

The solution, as is the case with many such procedural conversions, is to think recursively.

    def evolve(gen) do
      :random.seed
      evolve(gen, random_keyboards(16), 16, 0)
    end

    def evolve(gen, pop, size, best) do
      if gen == 0 do
        pop
      else
        newpop = List.concat(breed(p), p)
        newpop = natural_selection(newpop, size)

        first = Enum.at(childs, 0)
        display(first)

        if best < first.score do
          evolve(gen - 1, newpop, 16, first.score)
        else
          evolve(gen - 1, newpop, size + 2, best)
        end         
      end
    end

After working this out, a part of me has to agree the fanboys of functional languages. This does smack of an certain elegance that the procedure solution lacks. The solution is also efficient thanks to tail recursion, which avoids the usual call stack overhead of repeatedly calling nested functions.

But then there was the routine that I have yet to figure out how to do in a more functional way. To get the following routine to work I basically had to make Elixir behave much like a procedural language –running in a loop shuffling list indexes around.

    def cross(mother, father) do
      offset = 0

      if numeric_layout?() do
        offset = 9
      end

      n = number_of_actions(1)  # 0 or 1

      child_layout = Enum.reduce (0 .. n), mother.layout, fn(_, c) ->
        i = :random.uniform(length(c) - offset - 1) + offset
        l = Enum.at father.layout, i
        x = Enum.find_index(c, fn(q) -> q == l end)
        if x do
          swap(c, x, i)
        else
          c  # should generally never happen
        end
      end

      make_keyboard(mutate(child_layout))
    end

Perhaps it is inevitable that some procedural-esque code will always be present. In fact, that is the one caveat about functional programming that makes me wonder if maybe it’s not quite all that. Some problems, that are relatively easy to solve in a traditional procedural language, seem markedly harder to solve functionally.