在Ruby中队列而不是方法链和规则而不是条件

nit*_*jri 16 ruby paradigms haskell clojure

Rich Hickey在他的简单轻松谈话中描述了Clojure和Haskell的范例.作为一个ruby/rails程序员(这是我真正知道的),我喜欢他的想法,但不理解其中的两个:

  • 使用队列,而不是方法链接
  • 规则而不是条件

改为使用队列

显然,在Rails中我们喜欢方法链接,但是我想要了解Ruby中的Queue在他描述它的方式(54:54在视频中):

如果事物A调用事物B,那么你只需要它.你有什么时间和地点.A必须知道B在哪里才能调用B.当发生这种情况时,无论什么时候发生都是在A做的时候.在那里贴一个队列.

规则与条件

他谈到不使用条件或切换语句而是使用规则(30:00在视频中).

对于Ruby来说,我根本就不了解.如何在不使用条件的情况下做出决定?

谢谢大家,贾斯汀

Bey*_*mor 16

你好,队列

这里的想法是,我们可以通过在它们之间插入队列来解耦它们,而不是将值直接从一个对象传递到另一个对象.

假设我们正在模拟一位农民从鸡肉中采集鸡蛋.鸡生产鸡蛋,农民收集它们.当他们收集了五个鸡蛋时,农民的转变就完成了.通常,我们可能会写这样的东西:

class Chicken
    def initialize(name)
            @name = name
    end

    def lay_egg
            sleep random(3)
            "an egg from #{@name}"
    end
end

class Farmer
    def initialize(name, chicken)
            @name           = name
            @chicken        = chicken
    end

    def work_shift
            5.times do
                    egg = @chicken.lay_egg
                    puts "#{@name} got #{egg}"
            end
    end
end

betsy       = Chicken.new "Betsy"
fred        = Farmer.new "Fred", betsy
fred.work_shift
Run Code Online (Sandbox Code Playgroud)

因此,农民等待鸡肉,并在他们来的时候捡到鸡蛋.很好,问题解决了,去冰箱喝啤酒.但是,如果我们买了第二只鸡来增加鸡蛋产量呢?或者,如果我们想通过让他们从纸箱中捡蛋来测试我们农民的灵活性怎么办?

因为我们已经将农民编码为需要鸡肉,所以我们失去了做出这些决定所需的灵活性.如果我们可以解耦它们,我们将拥有更多的自由.

所以,让我们在它们之间插入一个队列.鸡会在滑道顶部产卵; 农民将从滑槽底部收集鸡蛋.任何一方都不直接依赖另一方.在代码中,可能如下所示:

class Chicken
    def initialize(name, chute)
            @name   = name
            @chute  = chute
            Thread.new do
                    while true
                            lay_egg
                    end
            end
    end

    def lay_egg
            sleep rand(3)
            @chute << "an egg from #{@name}"
    end
end

class Farmer
    def initialize(name, chute)
            @thread = Thread.new do
                    5.times do
                            egg = chute.pop
                            puts "#{name} got #{egg}"
                    end
            end
    end

    def work_shift
            @thread.join
    end
end

chute       = Queue.new
betsy       = Chicken.new "Betsy", chute
fred        = Farmer.new "Fred", chute
fred.work_shift
Run Code Online (Sandbox Code Playgroud)

除了现在,我们可以轻松添加第二只鸡.这是梦想的东西:

chute       = Queue.new
betsy       = Chicken.new "Betsy", chute
delores     = Chicken.new "Delores", chute
fred        = Farmer.new "Fred", chute
fred.work_shift
Run Code Online (Sandbox Code Playgroud)

你可以想象我们如何也可以用一堆鸡蛋装上一个滑道来测试农民.不需要嘲笑鸡,我们只是准备一个队列并传递它.

再见,条件

我对此的回答可能有点争议,但要短得多.你可以看看Ruby中的多方法,但是这个想法的关键是放弃封闭的,硬编码的逻辑路径,而不是开放的逻辑路径,事实上,普通的ol'多态性就是这样.

每当你调用某个对象的方法而不是打开它的类型时,你就会利用Ruby的基于类型的规则系统而不是硬编码逻辑路径.显然,这个:

class Dog
end

class Cat
end

class Bird
end

puts case Bird.new
when Dog then "bark"
when Cat then "meow"
else "Oh no, I didn't plan for this"
end
Run Code Online (Sandbox Code Playgroud)

不如此开放:

class Dog
    def speak
            "bark"
    end
end

class Cat
    def speak
            "meow"
    end
end

class Bird
    def speak
            "chirp"
    end
end

puts Bird.new.speak
Run Code Online (Sandbox Code Playgroud)

在这里,多态性为我们提供了一种描述系统如何使用不同数据行为的方法,这些数据允许我们随心所欲地为新数据引入新行为.所以,干得好,你(希望)每天都避免条件限制!


J. *_*son 5

这两点都不是Haskell非常好地体现的.我认为Haskell仍然导致一些未完成的代码,但是用不同的哲学和不同的工具来解决整个问题.

队列

粗略地说,Hickey想指出如果你在一个调用另一个对象的对象上编写一个方法

class Foo
  def bar(baz)
    baz.quux
  end
end
Run Code Online (Sandbox Code Playgroud)

那么我们只是硬编码了传入的内容Foo#bar必须有一个quux方法的概念.这是他的观点的一个比较,因为它意味着Foo实现本质上与实现传递的对象的Foo#bar实现有关.

这在Ruby中不是问题,其中方法调用更像是在对象之间发送的动态调度消息.它只是意味着传递给的对象Foo#bar在给定quux消息时必须以某种方式负责任地响应,而不是更多.

但它确实意味着消息处理的顺序性.如果改为发送的消息下一个队列,最终被传递到生成的对象,那么你可以很容易地放置一个中间人在那个缝---也许你想运行barquux兼任.

不仅仅是Haskell,这个想法在Erlang中被视为一个逻辑极端,我强烈建议学习Erlang如何解决这些问题.

 spawn(fun() -> Baz ! quux)
Run Code Online (Sandbox Code Playgroud)

规则

Hickey反复强调,特殊的,硬编码的分支方法可以收集东西.要指出,他不喜欢案例陈述或模式匹配.相反,他建议规则,我认为它是指"生产规则"系统.它们通过允许程序员为某些操作"触发"然后等待直到传入事件满足足以引发操作的规则来设置一组规则来产生选择和分支.这些想法最着名的实现是Prolog.

Haskell将模式匹配深深地融入其灵魂中,因此很难说Haskell会立即以这种方式进行解复...但是在Haskell 类型解析中存在一个非常好的规则系统示例.

可能最着名的概念是mtl样式类型类,你最终用签名来编写函数

foo :: (MonadReader r m, MonadState s m, MonadIO m, MonadCatch m)
    => a -> m b
Run Code Online (Sandbox Code Playgroud)

只要它遵循某些约束,它foo在类型中是完全多态的m- 它必须具有常量上下文r,可变上下文s,执行IO能力以及抛出和捕获异常的能力.

实例化所有这些约束的实际解决方案是通过(喜欢或其他)称为"类型类prolog"的规则系统来解决的.实际上,它是一个足够强大的系统来编码类型系统内的整个程序.

它实际上非常好,并为Haskell提供了一种自然的依赖注入风格,如上mtl例所述.

但是,我认为,在长时间使用这样的系统之后,大多数Haskeller都明白,虽然规则系统有时很聪明......但它们也很容易失控.Haskell 对类型类prolog的功能有很多谨慎的限制,这确保了程序员很容易预测它将如何解决.

这对于整个规则系统来说是一个主要问题:你失去了对最终解雇的行为的明确控制......所以按摩你的规则来实现你期望的那种结果变得更加困难.我实际上并不确定我同意Rich这里的规则系统因此导致了解包.您可能没有明确地分析与其他对象绑定的信息,但是您在事物之间设置了许多模糊的,远程的依赖关系.