仅使用类进行编程

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.

在我的文章《在“纯” Ruby中实现数字》中,我建立了一些基本规则,使我们可以使用一些基本的Ruby类东西,例如相等运算符,布尔值, nil和块等。

But what if we had absolutely nothing, even basic operators like if and while? Get ready for some pure OOP-madness.

但是,如果我们什么都没有,即使基本运算符也喜欢if and while怎么办? 准备一些纯粹的面向对象的疯狂。

  • 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.

    我们应该假设ruby没有预定义任何类。 想象我们从头开始。 甚至没有nil东西。

  • The only operator we can use is the assignment operator (x = something).

    我们可以使用的唯一运算符是赋值运算符( x = something )。

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.

考虑一下,在像Ruby逻辑表达式这样的动态语言中,实际上不需要评估为某个“布尔”类。 相反,除了某些特殊情况(在Ruby中为nilfalse ,在JS中为false0'' ),他们将所有内容都视为true。 因此,合并这种逻辑似乎并不自然。 但是,让我们深入了解它。

基础班 (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”.

内部的方法是我们的​​逻辑基础。 如您所见,我们立即假设任何对象都是真实的,因此我们返回“ then-branch”。

What about false? Let’s start with null, actually.

假的呢? 实际上,让我们从null开始。

class NullObject < BaseObject
  def if_branching(_then_val, else_val)
    else_val
  end
end

Same thing but it returns the 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:

在Ruby中,几乎每个类都继承自Object类。 但是,还有一个名为BasicObject的类,它在层次结构中甚至更高。 让我们模仿这种风格,并介绍我们替代Object

class NormalObject < BaseObject
end

Now, everything we define later on should inherit from NormalObject. Later on, we can add global helper methods there (like#null?).

现在,我们稍后定义的所有内容都应继承自NormalObject 。 稍后,我们可以在其中添加全局帮助器方法(例如#null? )。

如果表达式 (If-expressions)

This is enough for us to create our if-expressions:

这足以让我们创建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

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 our programs would be incredibly inefficient and even invalid without it.

这非常重要,因为如果没有它,我们的程序将效率极低,甚至无效。

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.

如您所见,在我们显式使用#call之前,不会执行实际的行为。 而已。 这就是我们可以通过If类执行复杂代码的方式。

布尔值(仅因为我们可以) (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.

在这里,我们有一个伞类,称为Bool ,没有实现的TrueObject (该对象的任何实例都被视为true)和FalseObject#if_branching以与NullObject相同的方式覆盖#if_branching

That’s it. We implemented booleans. I also added logical NOT operation for convenience:

而已。 我们实现了布尔值。 为了方便起见,我还添加了逻辑NOT操作:

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 the underlying object’s #if_branching method. Simple, yet incredibly useful.

如您所见,它只是翻转基础对象的#if_branching方法的参数。 简单,但非常有用。

循环 (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.

好的,编程语言中的另一重要内容是循环。 我们可以通过使用递归来实现循环。 但是,让我们实现一个显式的While运算符。

In general, the while operator looks like this:

通常, while运算符如下所示:

while some_condition
do_something
end

Which could be described like this: “if the 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!

需要指出的是,我们的条件应该是动态的-它应该能够在迭代之间进行更改。 “ Callables”进行救援!

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:

我们还需要一种行走的方法(这次没有#each +阻止!)。 让我们创建一个将处理它的类:

#
# 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).

我认为主要逻辑很简单。 我们还需要一些用于#head#tail helper-runnable来避免空指针错误(即使我们的空值实际上不是空值,我们仍然有可能对其调用错误的方法)。

计数器 (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.

需要注意的有趣事情是#inc_callable方法。 我认为,如果我们尝试使用这些基本类来实现自己的“语言”,则可能会添加一个带有_callable后缀的方法以返回“可调用”对象的约定。 这有点像在函数式编程中传递函数。

计算列表中的空值 (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?):

首先,我们需要一个空检查。 我们可以将其合并到NormalObjectNullObject作为辅助#null? (类似于Ruby的#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...

面向对象编程是一个非常有趣的概念,并且显然非常强大。 本质上,我们仅通过使用纯OOP而没有其他运算符来构建了一种编程语言(!)。 我们只使用类定义和变量。 另一个很酷的事情是,我们的语言中没有原始文字(例如,我们没有null ,而是实例化了NullObject )。 哦,编程的奇迹...

The code is available in my experiments repo.

该代码在我的实验存储库中可用。

Originally published at https://weird-programming.dev on September 17, 2020.

最初于2020年9月17日发布在https://weird-programming.dev

翻译自: https://medium.com/swlh/programming-only-with-classes-7005630f0e2b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值