Porting Clojure libraries to Ruby – Part 2

Last post I gave an introduction on how I’m working with GraalVM to port Clojure things to Ruby. Now, it’s time to handle the hard parts.

The first thing is – some stuff simply doesn’t work. Pathom3 (the library I wanted to port) depends on Guardrails, which requires core.async, which starts a thread pool, which GraalVM doesn’t like. At all.

And the second things is how to serialize some weird stuff like callbacks. So let’s start with the first part – how to make a “Pathom3 shared library” without GraalVM screaming at us

Porting Clojure libraries to Ruby

Since I started working with Ruby, in some situations I miss some of the Clojure libraries – especially killer ones like Pathom.

Unfortunately, rewriting the whole library in a different language is completely out of the question, especially because I will reintroduce some of the bugs that are already fixed, and will not have some of the more important features like parallel processing and other stuff.

But it’s not an “end of the world” situation. GraalVM have a very important feature that is to compile Java code to native, using what it’s usually called the “native-image” or, to be more precise, SubtrateVM. I also knew that you could generate shared libraries with this approach, but I never actually did anything and in my last attempts – every time I tried the only result I had was lots of frustrations and weird errors.

So that’s why I’m very proud and very surprised to announce that I have a Pathom working in Ruby right now.

But more on that later – it’s probably not yet ready to be used in production; but what I want to share is how I actually made it work, then challenges I faced (and how to handle them) and how can other people do the same. Finally, I am writing this post to make it easy for other people to do the same – honestly, the whole thing was awfully hard but not because it’s actually hard – but because the documentation is lacking so much that, at some times, I even though things I did were not possible.

My last experience with Ruby

On my last job I tried to go back to Ruby programming language. Not really by choice – but just because it was a language and was familiar with, and my last two jobs in Clojure didn’t really work out for me (not because of the language, really). I even imagined that some things would be easier in Ruby, specially while working with relational databases (something I really did miss while I was working in the Clojure language).

At the time, in my opinion, Rails was still a great framework – maybe a little too complex. And I imagined that somehow there could be better ways of doing Ruby code that could use the best parts of Rails and avoid the worst (like ActiveRecord, for example, maybe even substitute it with Sequel or ROM).

No a year and a half later I’m back to Clojure – and I will probably never go back to Ruby. And the reason is quite simple: I was working with Ruby doing exactly the same code that I did about three years ago. Rails didn’t change that much, but still was a huge mess when you had to upgrade from one version to another. The same problems I had in the past kept happening again and again, and it felt like a huge step backwards for me.

Rails’ ActiveRecord – the bad and the ugly

I’m known to not be a big fan of ActiveRecord. No, that would be a simplification: I probably hate ActiveRecord and think it adds more problems than it solves, specially after I began to work with functional programming and saw how difficult, if not utterly impossible, is to make ActiveRecord models behave like immutable structures or separate (and maybe even predict) the I/O from the rest of the code.

The ActiveRecord pattern (not the GEM) was created to hide SQL details from the users. The Gem elevates this to extremes: you never know when a query is issue, what query is issued (unless you check the logs), and sometimes a later clause modifies the way previous clauses work. Also, to extend ActiveRecord, you need to rely on monkey-patches and other internal implementation details, and there are API changes that seem innocent but are tremendously dangerous.

Now, what I want to do in this post is to elaborate the bad and the ugly parts. I’m not gonna talk about the “good parts” because we already know: auto-discovery of fields, fast prototyping, simple CRUDs, and so on. One could argue that this “easy setup, fast prototyping” is not worth the amount of technical debt you’ll have later, but let’s focus on the bad parts instead:

Understanding Ruby’s Awesome Nested Set

Recently, I saw people migrating from Awesome Nested Set to Ancestry. The reasons are simple – Ancestry is very simple, it just need a new string field in your table, and it’s easier to reason about. So, why should I even consider to use an alternative?

The answer is simple, and I’m going to quote H. L. Mencken: For every complex problem there is an answer that is clear, simple, and wrong.

And the reason that using Ancestry is a bad idea is simple: it doesn’t reflect good design. It treats a single field as a multi-valored column that keeps the ancestry of your object. This means that there’s no way to fetch all records AND their parents in a single query, or fetch all records AND their children, because we need to split the string (ruby-side) and then create a query (also ruby-side). So, in this post, I’ll show some tricks of what we can do with Awesome Nested Set, or even beter, how does it implements the tree pattern (and why it calls it a “set”, not a “tree”).

First, when we think about categories and sub-categories, we think like this:

But this is, in fact, a terrible way of representing trees in SQL. So, the alternative, is to transform it in a group of sets: Parent 1 (P1, for short), is a super-set containing subsets C1 and C2 (Children 1 and Children 2, for short). Each of the children have its own grandchildren, so C1 is a super-set containing G1, and C2 is a super-set containing G2 and G3.


Don’t be a Copperfield

After a year and a half working in other languages, I’m back to Ruby. And, one of the things that I was amazed at Clojure is how simple things were.

Back to Ruby, I’m really surprised on how people overcomplicate things.

There’s no perfect language, nor perfect community of languages. In Clojure, there are fewer abstractions, which is not exactly a good thing – if you, let’s say, just want to create a simple page to show data from a database, you’ll find it extremely tedious to do it in Clojure, where in Ruby/Rails it’s just a few lines of code away. Ruby’s moto is “programmer happiness”, and this reflects in every library that they write.

But this kind of higher abstraction pays off with time. At least where I live, working with Ruby means working with Rails almost all the time. There are no “big competitions” for Rails or ActiveRecord (Sequel is a close one, but at the time of this post, AR have 10 times more downloads than Sequel), and other web frameworks are mostly “Rails-like”. But what bugs me most is “magic”.

Programação funcional, imutabilidade, e previsibilidade

O post de hoje é uma introdução à programação funcional, para podermos entrar finalmente em Clojure. Mas antes disso, vamos falar sobre como aprendemos a programar na faculdade, em cursos, e em todos os lugares. Vamos falar de “orientação a objetos”, principalmente, e vamos falar sobre “abstração”. A programação, como sabemos, é um exercício total de abstração – ao fazer um software, temos apenas um objetivo – fazer com que um trabalho, que provavelmente seria realizado de forma ineficaz ou manual, torne-se automático. Parece uma super-simplificação, mas é verdade. Processadores de texto substituem máquinas de escrever, editores de imagem automatizam vários trabalhos de restauradores, pintores, e desenhistas, e sistemas de folha de pagamento substituem o trabalho de vários matemáticos, contadores, etc. A profissão de todos continua válida – apenas simplificamos um pouco (ou MUITO!) o trabalho deles. E o nosso trabalho, de programadores, é simplificado com linguagens mais modernas, nas quais se escreve menos e se sub-entende mais. E para isso, precisamos aprender a escrever nessas linguagens. E aí entram os cursos, ou a faculdade.

Basicamente, aprendemos a programar nesses cursos, ou na faculdade, pensando em orientação a objetos. Para muita gente, essa é a única maneira sadia de se programar – afinal, orientação a objetos é o paradigma que representa melhor o mundo real, uma frase que muito se ouve. E essa frase é verdadeira, mas com uma pegadinha muito difícil de encontrar: o mundo real é um lugar complicado.

Esse será um post grande, portanto, está dividido em partes. Falaremos sobre a imprevisibilidade, depois mutabilidade e imutabilidade, e na última parte teremos exemplos em Ruby e Clojure sobre trabalhar com dados mutáveis e imutáveis.

Interface Gráfica com Ruby

Quando se pensa em Ruby, logo se imagina usando Rails. Na verdade, Ruby é uma linguagem completa com suporte a praticamente qualquer coisa. Todo programador Ruby sabe disso, mas esquecemos isso em nosso dia-a-dia.

Muitas pessoas usam várias GEMs para tentar automatizar qualquer coisa – e isso é bem errado. Por exemplo, há várias gems que tentam trazer o ActiveRecord para Sinatra, enquanto que na verdade, tudo o que temos que fazer é o código abaixo:

  adapter: 'sqlite3',
  database: 'some_database.sqlite3'

E é a mesma coisa com GUI (Graphical User Interface). Há uma série de gems que tentam trazer uma interface gráfica “fácil” para nós, que na verdade só acabam complicando tudo. Por isso, temos às vezes que simplificar o processo – que no caso de interfaces gráficas, para mim significa usar a gem qtbindings

Evitando o null-driven-development

Quando a programação em C estava em alta, havia uma série de alocações e liberações de memória. Depois disso, a alocação/liberação passou para C++, e a partir daí tínhamos código como o seguinte:

Person *person = new Person();
delete person;

Algumas vezes, queríamos criar um objeto mas não tínhamos todas as informações dele. Era comum usarmos o ponteiro e só atribuir ele quando tivessemos a informação:

Person *person = null;
//do something in-between
person = new Person(name);

Isso causava um efeito estranho que, eventualmente, o objeto seria “nulo”, ou “não existente”. Isso era uma novidade até o momento, já que nas linguagens mais antigas (VB, QuickBasic, Pascal, etc) ou não havia esse conceito de “nulo” ou não era comum usar.

Quando as linguagens orientadas a objeto dominaram o mercado, esse “null-pattern” acabou também entrando no mercado. Em Java (e Scala), por exemplo, qualquer operação que envolva um null lança um “Null-pointer exception” (que muitos programadores simplesmente capturam com um try-catch, mandam imprimir no console o stacktrace, e continuam o programa, que normalmente para de funcionar). Em Ruby, as coisas são mais complexas…

Ruby é a primeira linguagem que eu conheço que meio que “institucionalizou” o uso de nulos.

SQL Orientado a Objetos

O nome parece estranho, mas um ORM, dependendo de como ele for implementado, pode ser usado exatamente para isso.

Estou trabalhando numa lib em Scala chamada relational, na qual eu pretendo fazer um SQL inteiro virar um objeto Scala. Mais ou menos o que o Arel tenta fazer, porém de forma esquisita (meio compatível com Rails, meio compatível com álgebra relacional, e não 100% nada). Mas isso fica pra um outro momento…

No post anterior, eu falei bastante sobre SQL, e sobre todas as coisas que podemos fazer ao saber montar uma query. A idéia agora é tentar montar, de fato, uma query, mas com mais do que apenas fragmentos SQL, mas com o próprio ORM.

Vamos pensar que temos uma tabela de usuários, e uma de números de telefones. O número pertence a um usuário, um usuário tem muitos números de telefone (nada de “join-tables” e coisas mais complexas por agora). Digamos que eu queira saber números de telefone possuem o mesmo prefixo (os primeiros quatro números-vamos ignorar, por hora, os nono dígito para deixar o código mais fácil) de um determinado número.

A idéia, num primeiro momento, é fazer o código para um único número. Vamos, por simplicidade, deixar isso na classe de Telephone mesmo:

class User < ActiveRecord::Base
  has_many :telephones

class Telephone < ActiveRecord::Base
  belongs_to :user
  def self.same_prefix_of(telephone)
    where('SUBSTR(telephones.number, 0, 5) = ?', telephone.number[0...4])

#Para usar:

Por hora, tudo bem. Um código simples, porém é agora que a coisa começa a ficar divertida: generalização