Erlang Thursday – The digraph module

今天的Erlang Thursday我们开始看 digraph 模块。

当我研究这个模块的时候,发现这个模块并不和我预料的Erlang行为一样,所以我想在研究它的函数前仔细看看它那些与众不同的地方。

如果我们查看所有digraph模块的函数,我们只能找到两个函数 digraph:new/0 和 digraph:new/1 返回 digraph 类型数据。

我想看看这个Erlang API有什么奇怪的,我打开Erlang shell,添加一个节点到有向图然后检查这个操作的结果。

1
2
3
4
G = digraph:new().
% {digraph,69651,73748,77845,true}
G2 = digraph:add_vertex(G, foo, bar).
% foo

调用 digraph:add_vertex/3 返回的结果是foo,也就是传给它的第二个入参,这个结果并不像一个有向图的样子。
那么,再回过头来看看变量G是否有变化。

1
2
G.
% {digraph,69651,73748,77845,true}

元组形式的结果看起来和原来一样,那么我们看看那个节点是否在图里,因为我们获得返回值是foo。

1
2
3
4
digraph:vertices(G).
% [foo]
digraph:vertex(G, foo).
% {foo,bar}

看来那个节点的确在图中。

让我们再加入另一个节点到与变量G绑定到图中。

1
2
3
4
V = digraph:add_vertex(G).
% ['$v'|0]
digraph:vertices(G).
% [['$v'|0],foo]

这个节点也添加成功。

困惑所在

所以在研究这个模块的函数之前在本文中我要重点讲出来的这个行为模式是这些函数在图中显而易见地展现了可修改的行为模式。

我说它是显而易见的可修改的,因为如果它在底层的实现上是不可修改的,结构可以改变同时绑定的变量的引用保持不变。

1
2
3
4
5
6
7
8
9
10
digraph:vertices(G).
% [['$v'|0],foo]
Copy = G.
% {digraph,69651,73748,77845,true}
V2 = digraph:add_vertex(G, wat).
% wat
digraph:vertices(Copy).
% [['$v'|0],foo,wat]
digraph:vertices(G).
% [['$v'|0],foo,wat]

甚至其他变量引用也改变,所以它颠覆了我见过的在Erlang生态圈里关于所有数据是不可变的任何常规。

我们将在后续的Erlang Thursday文章中研究digraph模块的函数,但是我想花点时间讲这个模块中的可变性的内在本质,所以当你需要使用这个模块的时候,你一定要认识到如果不谨慎的话你想在你的应用的并发部分使用它将很危险。

修改 (10月18日)

做为 LFE Fridaystranslation into Lisp Flavored Erlang 的一部分,Robert Virding 告诉我这个模块的可变性的原因,这个内容也包含在他翻译本文的文章里。

屠龙(解惑)

这背后的原因是这个模块是如何实现的。一个 有向图 底层的实现上有三个 ETS 表。在上述例子里,这些表的id是 8207,12304和16401。你可以调用 ets:i/0 来看到,这个函数列出所有当前ets表。你可以看到那3个表属于Erlang shell进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> self().
<0.28.0>
> ets:i().
id name type size mem owner
----------------------------------------------------------------------------
1 code set 282 10393 code_server
4098 code_names set 64 7713 code_server
8207 vertices set 3 328 <0.28.0>
12304 edges set 0 305 <0.28.0>
16401 neighbours bag 2 319 <0.28.0>
ac_tab ac_tab set 6 839 application_controller
file_io_servers file_io_servers set 0 305 file_server_2
global_locks global_locks set 0 305 global_name_server
global_names global_names set 0 305 global_name_server
...
ok

有向图它自己的结构仅是一个标签元组,包含着ets表id。所有的改变都发上在ETS表里,图的结构本身从未改变。表的数据和内容可以用 ets:info/1ets:i/1 来读取。

原文链接: https://www.proctor-it.com/erlang-thursday-the-digraph-module/