创建像Active Record或Hibernate这样的ORM会很好,它应该像这样处理链式查询:
jar
我们怎么做?或者就像那样,在一条线上 - 或者至少在精神和意义上相似。
也许有一些预处理工具可以帮助解决这个问题?
答案 0 :(得分:7)
"链接"是一种混淆的功能组合形式,有时依赖于返回值,有时直接在状态突变上运行。这不是Erlang的工作方式。
考虑:
f(g(H))
相当于:
G = g(H),
f(G)
但可能或可能不等同于:
g(H).f()
在这种形式下,功能被叠加,操作继续进行"朝向"返回值侧(在大多数编程语言中几乎总是左侧)。在程序员中作为唯一可用范例(例如Java)的OOP 强制的语言中,由于要求所有功能,这种功能流动的形式通常不可能没有过多的噪声(类方法)基本上由类名命名,无论是否涉及任何对象:
F.f(G.g(h.get_h()))
更常见的是,对数据的Java操作会添加到对象本身,并且尽可能多的数据保存在对象实例中。变换方法没有" return"以相同的方式改变价值,他们改变内部状态,留下相同的对象,但是新的版本"它的。如果我们将变异方法视为"返回对象的新版本"那么我们可以将点运算符看作是一个混淆的功能组合运算符,它使值排序为#34;流向右边"因为变异对象现在正在调用其他方法,这可能会随着事情的发展而继续改变状态:
query.prepare(some_query).first(100).sort("asc")
在这种情况下执行" flow"向右移动,但仅仅因为功能组合的概念已经丢失 - 我们正在"滴答作响"沿着一系列变异状态事件转发而不是使用实际返回值。这也意味着在这些语言中,如果我们采用堆叠太远,我们可以在执行方向上获得一些非常奇怪的触发器:
presenter.sort(conn.perform(query.prepare(some_query)).first(100), "asc")
快速,一目了然告诉我最重要的内容是什么?
这不是一个特别好的主意。
Erlang没有对象,它有进程,并且它们不会像你想象的那样工作。 (这里将详细讨论:Erlang Process vs Java Thread。)Erlang进程不能相互调用或相互执行操作 - 它们只能发送消息,生成,监视和链接。就是这样。因此,没有办法进行"隐含的回报"值(例如在一组变异对象值的情况下)要在其上定义一个操作。 Erlang中没有隐式返回:Erlang中的每个操作都有一个显式返回。
除了显式返回和隔离内存并发进程的计算模型而不是共享内存对象之外,Erlang代码通常被写入"快速崩溃"。 大多数时间这意味着几乎所有具有副作用的函数都会返回元组而不是裸值。为什么?因为赋值运算符也是匹配运算符,它也使它成为断言运算符。程序中具有副作用的部分中的每一行通常以断言操作成功或返回预期类型或立即崩溃的方式编写。通过这种方式,我们可以准确地捕获故障发生的位置,而不是继续处理可能有故障或丢失的数据(这在OOP链式代码中始终发生 - 因此严重依赖异常来打破不良情况)。
使用你的代码,我不确定执行(以及我的眼睛,当我读到它)是否应该从左向右流动,或者相反:
User = User:new():for_login(«stackoverflow_admin»):for_password(«1984»):load().
在Erlang中 的等价物是:
User = load(set_password(set_uid(user:new(), "so_admin") "1984"))
但那只是愚蠢的。这些神秘的文字来自哪里?我们打算在线调用它们吗?
User = load(set_password(set_uid(user:new(), ask_uid()) ask_pw()))
如果用户输入无效值(无任何内容)或断开连接,或超时等等,那么提取自己将会非常尴尬。当找到一个角落案例时调试也会很难看 - 什么调用哪个部分失败以及与实际问题无关的多少东西现在正在等待返回值? (这是例外的地方...更多关于下面醒来的噩梦。)
相反,解决这个问题的常用方法类似于:
register_new_user(Conn) ->
{ok, Name} = ask_username(Conn),
{ok, Pass} = ask_password(Conn),
{ok, User} = create_user(Name, Pass),
User.
我们为什么要这样做?因此,我们知道何时崩溃确切地发生了 - 这将告诉我们它是如何以及为什么发生的。如果任何返回值不是形状{ok, Value}
的元组,那么进程将在那里崩溃(并且大多数时候这意味着与它进行连接 - 这是一件好事)。考虑在像Java这样的语言中处理像实际这样的副作用过程有多少异常。长链单线突然变得很多更多线:
User =
try
User:new():for_login("so_admin"):for_password("1984"):load()
catch
{error, {password, Value}} ->
% Handle it
{error, {username, Value}} ->
% Handle it
{error, db_create} ->
% Handle it
{error, dropped_connection} ->
% Handle it
{error, timeout} ->
% Handle it
%% Any other errors that might possible happen...
end.
这是不确定(甚至过长)组合的一个超级恼人的结果:它将所有错误情况堆叠在一起,并且必须通过传播异常来处理它们。如果您不在这些调用中的上述错误情况下抛出异常,那么您就不知道哪里出了问题,并且您无法通知该进程或其管理器执行应该终止,重试等。单行解决方案的成本至少仅在这一个程序中添加了,并且我们甚至没有解决这些错误中应该发生的事情处理程序!
这是Erlang"让它崩溃的光辉"哲学。只要我们断言那些假设,代码就可以用只做成功假设的方式编写。错误处理代码可以从其他地方(主管)中提取出来,并且当出现问题时,状态可以恢复到主业务逻辑之外的已知条件。拥抱它会创建强大的并发系统,忽略它会产生脆弱的晶体。
然而,所有这些的成本是那些单行断言。在并发程序中,这是一个深刻有益的权衡。
答案 1 :(得分:5)
虽然我发现@zxq9回答的信息如此丰富,但提到在Erlang中模仿类似Java的OOP风格的历史可能会有所帮助。
方法链需要 state 并且努力将“Stateful Modules”包含到Erlang中:
Parametrized Module:一种提议,允许开发人员实现接受参数的模块,并将其作为模块状态保存在其函数中。很久以前它已被添加到Erlang作为实验性功能,但技术委员会decided to remove在R16中对此功能的语法支持,因为它导致了概念和实际的不兼容性。
Tuple Module:参数化模块的向后兼容性,在Programming Erlang (Second Edition)的第8章中也详细介绍,但没有官方文档,它仍然是{{3这引入了复杂性和模糊性而没有Erlang方式的力量。
在Erlang中模仿具有不同范例(如Ruby和Java)的语言编码风格,这是一种具有不同概念的函数式语言,可能会令人兴奋但没有附加价值。
答案 2 :(得分:1)
看看BossDB。它是一个编译器链和运行时库,用于通过Erlang参数化模块访问数据库。