将脚本从OOP转换为FP,我试图了解如何将“方法包”(类)替换为“过程包”。 Procs是具有Binding的那些可组合(<<),匿名微对象,但是我没有看到使用多个#call的示例。我谦虚的脚本只有几十种方法:用proc替换它们需要某种容器来组织代码。
试图将Procs存储到哈希中。但是Hash在设计时并未考虑到此应用程序。例如,从同一散列内的另一个proc调用proc是很尴尬的。
试图将Procs存储到与变量关联的类中。但是封装降低了我访问外部变量的能力(失去了闭包的一项主要好处),并使对proc的访问变得复杂(使用实例变量必须实例化类,或者使用类变量必须创建类访问器方法)。
试图将Procs存储到方法中,但是获得的好处是什么。
require 'net/http'
require 'json'
require 'iron_cache'
require 'discordrb'
class Config
# config vars
end
data_space = {
:message => ""
}
bot = Discordrb::Bot.new(token: Config::DISCORD_TOKEN, ignore_bots: true)
channel = bot.channel(Config::DISCORD_CHANNEL_ID)
cache = IronCache::Client.new.cache("pa")
bot_control = {
:start => proc {bot.run(:async)},
:stop => proc do
bot.stop
exit(0)
end,
:send => proc {data_space[:message].then {|m| bot.send_message(channel, m)}},
"messaging?" => proc do |wish|
[ Config::MESSAGING[wish.first] , Config::NIGHTTIME_FLAG ].compact.first
end
}
cache_control = {
:get_flag => proc {|flag| cache.get(flag)&.value},
:set_wish => proc do |wish, exp_sec|
cache.put("messaging", wish.to_json, :expires_in => exp_sec)
data_space[:message] << "#{wish.first} got it!"
end,
:exp_sec => proc do
t = Time.now
(t-Time.new(t.year,t.month,t.day,Config::EXP_REF,0,0)).to_i
end,
:get_wish => proc do
msg = proc {cache_control[:get_flag].call("messaging")}
msg ? JSON.parse(msg) : [nil, nil]
end
}
bot_control[:start].call
data_space[:message] << "ah"
bot_control[:stop].call if (bot_control["messaging?"]<<cache_control[:get_wish]).call
(bot_control[:stop]<<bot_control[:send]).call
实际交付。
但是我认为不是可读的那种代码。还是对OOP的改进。 请告知。
看起来我尝试使用哈希将proc分组的方法是正确的。如elsewhere所述,Proc中的哈希将实例方法添加为#count,#keys和#store来访问和操作它。因此,我可以这样写:
bc = proc { |args|
{
start: proc {...},
stop: proc {...}
}
}
bot_control = bc.call
bot_control[:start].call
bot_control[:stop].call
有一些警告。哈希返回的值是proc,并且添加了组合方法(<<和>>),但它们不起作用。另外,我还没有找到如何从哈希中引用其他值。尽管可以添加方法(默认情况下为私有,但可以声明为公共),并可以从值中引用它们。
因此,我获得了嵌套的闭包(变量一直向下泄漏),但此时的代码与常规类没有太大的不同。 #new已成为#call(毕竟Proc从Method继承),但是FP方向没有太大改进。请指教。
答案 0 :(得分:2)
使用模块和常量。
module BotControl
Start = proc {bot.run(:async)}
Stop = proc do
bot.stop
exit(0)
end
Send = proc {DataSpace::Message.then {|m| bot.send_message(channel, m)}}
Messaging = proc do |wish|
[ Config::MESSAGING[wish.first] , Config::NIGHTTIME_FLAG ].compact.first
end
end
请注意,此代码无法按原样工作:bot
变量不可见!有两种方法可以解决此问题,但它们不在此问题的范围内(我的2¢,它应该是这些函数的参数)。
用法看起来像
(BotControl::Messaging<<CacheControl::GetWish).call
您的函数应该是常量,因此可以保护您免于重新分配(散列不会)。同样,模块不能被实例化/继承,因此避免了任何类型的OOPness。另一个好处是可以包含模块,因此,如果您希望将某些功能置于顶层,则可以轻松地做到这一点。
您可能会想使用匿名模块来做到这一点,例如
BotControl = Module.new do
Start = proc {bot.run(:async)}
...
end
但是这是行不通的,因为Ruby中常量的范围是 lexical ,即在 parsing 阶段而不是在运行时,它们位于哪个模块很重要。在上面的示例中,在解析器级别,Start
只是顶级,因为它不在任何模块内(Module.new
只是一个方法调用,它在运行时进行评估)。因此,这种方法实际上并没有将任何东西组合在一起。