Functional programming 我应该采取多大程度的参考透明度?

Functional programming 我应该采取多大程度的参考透明度?,functional-programming,erlang,mnesia,Functional Programming,Erlang,Mnesia,我正在使用erlang、mnesia和webmachine构建一个网站。我读过的大多数文档都称赞具有引用透明函数的优点 问题是,所有数据库访问都是外部状态。这意味着任何命中数据库的方法不再是引用透明的 假设我在数据库中有一个用户对象和一些处理身份验证的函数 引用不透明函数可能如下所示: handle_web_request(http_info) -> is_authorized_user(http_info.userid), ... %referentially opaque is

我正在使用erlang、mnesia和webmachine构建一个网站。我读过的大多数文档都称赞具有引用透明函数的优点

问题是,所有数据库访问都是外部状态。这意味着任何命中数据库的方法不再是引用透明的

假设我在数据库中有一个用户对象和一些处理身份验证的函数

引用不透明函数可能如下所示:

handle_web_request(http_info) ->
  is_authorized_user(http_info.userid),
  ...
%referentially opaque
is_authorized_user(userid) ->
  User = get_user_from_db(userid),
  User.is_authorized.

%referentially opaque
lots_of_other_functions(that_are_similar) ->
  db_access(),
  foo.
引用透明性要求我最小化引用不透明代码的数量,因此调用方必须从数据库获取对象,并将其作为参数传递给函数:

handle_web_request(http_info) ->
  User = get_user(http_info.userid),
  is_authorized_user(User),
  ...

%referentially opaque
get_user(userid) ->
  get_user_from_db(userid).

%referentially transparent      
is_authorized(userobj) ->
  userobj.is_authorized.

%referentially transparent    
lots_of_other_functions(that_are_similar) ->
  foo.
上面的代码显然不是生产代码——它纯粹是为了说明目的而编写的


我不想陷入教条。引用透明代码(如可证明的单元测试)的好处是否证明了界面不够友好?在追求引用透明性方面,我应该走多远?

您已经提到了单元测试,请继续用这些术语思考。您在测试中发现的所有有价值的东西都应该是引用透明的,这样您就可以对其进行测试

如果您没有任何可能出错的复杂逻辑,并且一次功能/集成测试就会发现它是正确的,那么为什么还要费心进行额外的测试呢


想想看。但是在真正需要单元可测试性的地方。

为什么不一直采用引用透明性呢

考虑
get\u user\u from\u db
的定义。它如何知道如何与数据库通信?显然,它假设了一些(全局)数据库上下文。您可以更改此函数,使其返回一个以数据库上下文为参数的函数。你所拥有的是

get_user_from_db :: userid -> User
这是一个谎言。您不能从用户ID转到用户。你还需要别的东西:一个数据库

get_user_from_db :: userid -> Database -> User

现在只需使用userid来实现这一点,并在稍后的某个时间提供一个数据库,该函数将为您提供一个用户。当然,在现实世界中,
数据库
将是一个句柄或数据库连接对象或其他对象。为了进行测试,给它一个模拟数据库。

现在我已经阅读了您的答案,它看起来非常明显。但是我从来没有想过——谢谢!然而,在我看来,(在这种情况下)编写返回具有数据库上下文的函数更有意义:get_user_from_db::database->userid->user我认为这个版本可能会破坏引用透明性,因为基础数据库上下文可能会更改…您可以从另一个中获取一个<代码>a->b->c相当于
b->a->c
。您所需要的只是一个高阶函数:
flip f a b=f b a
。设计选择中的参数顺序。在这种特殊情况下,将数据库作为最后一个参数是有意义的,因为这允许您进行Kleisli合成。将
数据库
作为第一个参数,函数只能由返回数据库的函数组成。我想没有太多。你可以去掉整个数据库,只有一个循环(MyWhistState)->循环(receive X->X end,MyWhistState)和循环(RecvHttp,MyWhistState)->,循环(MyWholeNewState)函数,使所有引用都透明。
get_user_from_db :: userid -> Database -> User