【发布时间】:2019-09-06 18:33:52
【问题描述】:
将脚本从 OOP 转换为 FP,我试图了解如何用“过程包”替换“方法包”(类)。 Procs 是那些可组合的(
试图将 Procs 存储到哈希中。但是 Hash 在设计时并未考虑到此应用程序。例如,从同一个哈希内的另一个 proc 调用一个 proc 很尴尬。
试图将 Procs 存储到与变量关联的类中。但是封装降低了我访问外部变量的能力(失去了闭包的一个关键好处)并且使访问过程变得复杂(实例变量必须实例化类或类变量必须创建类访问器方法)。
尝试将 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 的改进。 请指教。
编辑
看起来我尝试使用 Hash 对 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
有一些警告。哈希返回的值是 procs 和组合方法(>)被添加,但它们不起作用。此外,我还没有找到如何从散列中引用其他值。虽然可以添加方法(默认情况下是私有的,但可以声明为公共的)并从值中引用它们。
所以我获得了嵌套闭包(变量一直向下泄漏),但此时的代码与常规类没有太大区别。 #new 变成了#call(毕竟 Proc 继承自 Method),但在 FP 方向上并没有太大的改进。请指教。
【问题讨论】:
-
为什么不直接使用类方法(比如其他语言中的“静态”方法)?
-
@maxpleaner 我认为 OP 希望函数具有函数组合运算符,而 Ruby 的方法并不完全支持
-
您可能希望使用更简洁的 JavaScript 样式的哈希声明:
{ start: ... }而不是{ :start => ... },因为它从 Ruby 1.9 开始就已经存在并且现在得到完全支持。旧的 1.8 样式仅在使用字符串键之类的东西时才需要。 -
我在这里写了一个关于从“FP”返回到 OOP 的答案,因为我不确定这里的目标是什么。正如您所说,这不是函数式编程,它只是一个“函数包”。 “函数式编程”通常指的是对于任何给定的参数集总是产生相同输出的函数,即它们是无状态的。使用闭包来封装状态会完全破坏它。你这里的风格不是“功能性的”,但它肯定是带有大量 JavaScript 风格的。
-
@tadman 绝对同意这不起作用,但它很接近并且不那么难修复。
标签: ruby functional-programming