In my post Implementing numbers in "pure" Ruby ( "Develop a number on a" pure "Ruby"), I outlined a framework that permits the use of basic things like Ruby's equality operator, true
/ false
, nil
, blocks, etc.
But what if we had nothing at all? Even basic operators like if
and while
? Get ready for a dose of pure object-oriented madness.
Framework
- We can define classes and methods
- , Ruby . , .
nil
- , — (
x = something
).
if-? ? !
— . ? :
, Ruby - "Boolean". , (nil
false
Ruby;
false
, 0
''
JS). , . .
, :
class BaseObject
def if_branching(then_val, _else_val)
then_val
end
end
if_branching
— . , , , then_val.
? null:
class NullObject < BaseObject
def if_branching(_then_val, else_val)
else_val
end
end
, .
Ruby Object
. BasicObject
, . Object
:
class NormalObject < BaseObject
end
, NormalObject
. ( #null?
).
If-
, if-:
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
! . .
:
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
, : " , ?". ""?
:
#
if today_is_friday?
order_beers()
else
order_tea()
end
# If
If.new(today_is_friday?, order_beers(), order_tea()).result
. - , .
() , .. .
. (. "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
, , #call
. . If
.
( , )
(null ), . :
class Bool < NormalObject; end
class TrueObject < Bool; end
class FalseObject < Bool
def if_branching(_then_val, else_val)
else_val
end
end
Bool
, TrueObject
( , .. )
FalseObject
, #if_branching
, NullObject
.
. . :
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
- "" #if_branching
. , .
, — . . While
.
:
while some_condition
do_something
end
: " , ".
, — . !
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
# "" While#run.
#
# (, )
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
, null-.
:
class List < NormalObject
def initialize(head, tail = NullObject.new)
@head = head
@tail = tail
end
def head
@head
end
def tail
@tail
end
end
- ( #each
!). , :
#
#
#
class ListWalk < NormalObject
def initialize(list)
@left = list
end
def left
@left
end
# current.
# null
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
, . #head
#tail
, null-pointer ( , null null, ).
, :
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
— #inc_callable
. , "" , , _callable
. - .
null
null. NormalObject
NullObject
#null?
( #nil?
Ruby):
class NormalObject < BaseObject
def null?
FalseObject.new
end
end
class NullObject < BaseObject
def null?
TrueObject.new
end
end
null-:
#
# , NullObject
#
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
. null- .
- — , , . , , (!), - . , : . — (, null
, NullObject
). , ...