In my post Implementing numbers in “pure” Ruby I established some ground rules that allowed us to use some basic ruby stuff like equality operator, booleans, nil, blocks and so on. But what if we had absolutely nothing, even basic operators like if and while? Get ready for some pure OOP-madness. Ground rules We can define classes and methods. We should assume that ruby doesn’t have any classes predefined. Imagine we start from scratch. Even stuff like nil is not available. The only operator we can use is the assignment operator (x = something). No if-operator? Seriously? Even CPUs have it! Conditionals are important. They are the very essence of logic for our programs. So how do we do without them? I’ve come up with a solution: we can incorporate boolean logic inside EVERY object. Think about it, in dynamic languages like Ruby logical expressions don’t actually need to evaluate into some “Boolean” class. Instead, they treat everything as true except for some special cases (nil and false in Ruby, false, 0 and '' in JS). So incorporating this logic doesn’t seem that unnatural. But let’s dive right into it. Basic classes Let’s create a very basic class that will be the ancestor to everything we build in the future: class BaseObject def if_branching(then_val, _else_val) then_val end end The method inside is our logical foundation. As you can see, straight away we assume that any object is true so we return “then-branch”. What about false? Let’s start with null, actually. class NullObject < BaseObject def if_branching(_then_val, else_val) else_val end end Same thing but it returns second parameter. In Ruby almost every class inherits from Object class. However, there’s another class called BasicObject which is even higher up in the hierarchy. Let’s copycat this style and introduce our alternative to Object: class NormalObject < BaseObject end Now, everything we define later on should inerit from NormalObject. Later on we can add global helper methods there (like #null?). If-expressions This is enough for us to create our if-expressions: class If < NormalObject def initialize(bool, then_val, else_val = NullObject.new) @result = bool.if_branching(then_val, else_val) end def result @result end end And that’s it! I’m serious. It just works. Consider this example: class Fries < NormalObject end class Ketchup < NormalObject end class BurgerMeal < NormalObject def initialize(fries = NullObject.new) @fries = fries end def sauce If.new(@fries, Ketchup.new).result end end BurgerMeal.new.sauce # ==> NullObject BurgerMeal.new(Fries.new).sauce # ==> Ketchup You may be wondering, how is that useful if we can’t pass any code blocks around. And what about the “laziness”? Consider this: # Pseudo-code if today_is_friday? order_beers() else order_tea() end # Our If class If.new(today_is_friday?, order_beers(), order_tea()).result In our example we will order beers AND tea disregarding the day of the week. This is because arguments are evaluated before being passed to the constructor. This is very important because without it our programs would be incredibly inefficient and even invalid. The solution is to wrap a piece of code in another class. Later on I will refer to this kind of wrappers as “callable”: class OrderBeers def call # do something end end class OrderTea def call # do something else end end If.new(today_is_friday?, OrderBeers.new, OrderTea.new) .result .call As you can see, the actual behaviour is not being executed until we explicitly use #call. That’s it. This is how we can execute complex code with our If class. Booleans (just because we can) We already have logical values (nulls and everything else) but it would be nice for expressiveness to add explicit boolean values. Let’s do that: class Bool < NormalObject; end class TrueObject < Bool; end class FalseObject < Bool def if_branching(_then_val, else_val) else_val end end Here we have an umbrella class called Bool, TrueObject with no implementation (any instance of this object is already considered true) and FalseObject that overrides #if_branching in the same way NullObject does. That’s it. We implemented booleans. I also added logical NOT operation for convenience: class BoolNot < Bool def initialize(x) @x = x end def if_branching(then_val, else_val) @x.if_branching(else_val, then_val) end end As you can see, it just flips parameters for underlying object’s #if_branching method. Simple, yet incredibly useful. Loops Okay, another important thing in programming languages is looping. We can achieve looping by using recursion. But let’s implement an explicit While operator. In general, the while operator looks like this: while some_condition do_something end Which could be described like this: “if condition is true, do this and repeat the cycle again”. The interesting thing to point out is that our condition should be dynamic - it should be able to change between iterations. “Callables” to the rescue! class While < NormalObject def initialize(callable_condition, callable_body) @cond = callable_condition @body = callable_body end def run is_condition_satisfied = @cond.call If.new(is_condition_satisfied, NextIteration.new(self, @body), DoNothing.new) .result .call end # Calls body and then runs While#run again. # This way looping is done recursively (too bad no tail-call elimination) class NextIteration < NormalObject def initialize(while_obj, body) @while_obj = while_obj @body = body end def call @body.call @while_obj.run end end class DoNothing < NormalObject def call NullObject.new end end end Sample program Let’s create some lists and a function that counts how many nulls in a given list. List Nothing special here: class List < NormalObject def initialize(head, tail = NullObject.new) @head = head @tail = tail end def head @head end def tail @tail end end We also need a way to walk it (no #each + block this time!). Let’s create a class that will be handling it: # # Can be used to traverse a list once. # class ListWalk < NormalObject def initialize(list) @left = list end def left @left end # Returns current head and sets current to its tail. # Returns null if the end is reached def next head = If.new(left, HeadCallable.new(left), ReturnNull.new) .result .call @left = If.new(left, TailCallable.new(left), ReturnNull.new) .result .call head end def finished? BoolNot.new(left) end class HeadCallable < NormalObject def initialize(list) @list = list end def call @list.head end end class TailCallable < NormalObject def initialize(list) @list = list end def call @list.tail end end class ReturnNull < NormalObject def call NullObject.new end end end I think the main logic is quite straightforward. We also needed some helper-runnables for #head and #tail to avoid null-pointer errors (even though our nulls aren’t actually nulls, we still risk calling a wrong method on them). Counter This is just an increment that will be used for counting: class Counter < NormalObject def initialize @list = NullObject.new end def inc @list = List.new(NullObject.new, @list) end class IncCallable < NormalObject def initialize(counter) @counter = counter end def call @counter.inc end end def inc_callable IncCallable.new(self) end end We don’t have any numbers and I decided not to waste time implementing them so I just used lists instead (see my post on implementing numbers here). An interesting thing to note is #inc_callable method. I think if we are to try and implement our own “language” with those basic classes, it could be a convention to add methods with _callable postfix to return a “callable” object. This is somewhat like passing functions around in functional programming. Counting nulls in list First of all we need a null-check. We can incorporate it within NormalObject and NullObject as a helper #null? (similar to Ruby’s #nil?): class NormalObject < BaseObject def null? FalseObject.new end end class NullObject < BaseObject def null? TrueObject.new end end Now we can finally implement our null-counter: # # Returns a counter incremented once for each NullObject in a list # class CountNullsInList < NormalObject def initialize(list) @list = list end def call list_walk = ListWalk.new(@list) counter = Counter.new While.new(ListWalkNotFinished.new(list_walk), LoopBody.new(list_walk, counter)) .run counter end class ListWalkNotFinished < NormalObject def initialize(list_walk) @list_walk = list_walk end def call BoolNot.new(@list_walk.finished?) end end class LoopBody < NormalObject class ReturnNull < NormalObject def call NullObject.new end end def initialize(list_walk, counter) @list_walk = list_walk @counter = counter end def call x = @list_walk.next If.new(x.null?, @counter.inc_callable, ReturnNull.new) .result .call end end end And that’s it. We can pass any list to it and it will count how many nulls that list has. Conclusion Object-Oriented Programming is incredibly interesting concept and, apparently, very powerful. We’ve, essentially, built a programming language (!) by using only pure OOP with no additional operators. All we used was class definitions and variables. Another cool thing is that we have no primitive literals in our language (e.g. we don’t have null, instead we just instantiate NullObject). Oh, wonders of programming… The code is available in my experiments repo.