Erlang的ms_transform模块文档

分享Erlang by 达达 at 2010-09-08

花了些时间翻译了erlang的ms_transform模块文档,希望对大家有所帮助。

原文地址:猛击打开链接

模块

ms_transform

概要

本模块通过parse_fransform机制,在编译时将“高阶函数”(fun)解析并翻译为“匹配规格”(match specifications)。

描述

本模块实现了parse_transform,以将ets和dbg的 fun2ms/1 调用转化为文字匹配的规格。它还为来自于erlang shell的调用实现了相同函数的后端。

高阶函数到匹配规格的翻译是通过调用两个“伪函数”:ets:fun2ms/1 和 dbg:fun2ms/1 实现的。

警告:要使用伪函数触发翻译,首先必须在代码中包含头文件 ms_transform.hrl。不这样做,将可能导致运行时错误而不是编译时,因为没有翻译的erlang程序可能被当成合法的表达式而不会触发编译时错误。

警告:被翻译的高阶函数必须在伪函数的参数列表中构造,而不能先绑定到一个变量再将变量传递给 ets:fun2ms 或 dbg:fun2ms ,即这种情况是合法的:ets:fun2ms(fun(A) -> A end) 而这种情况是不合法的:F = fun(A) -> A end, ets:fun2ms(F). 在正确包含了头文件的情况下后者将导致一个编译时错误,否则将在运行时出错。即使后面这种构造可以看起来正常运行,但事实不那样,所以请用于不要使用后一种构造。

对于被翻译成匹配规格的高阶函数有若干的限制。简单说来,你不能在fun中使用任何match_spac所不能使用的东西。这意味着,除了其它外,fun主要有下列几点限制:

  • 不能调用erlang编写的任何函数,不论是本地函数、全局函数或是真正的高阶函数。

  • 所有函数调用将被翻译成匹配规格的内置函数调用,因此调用 is_list(X) 将被翻译成 {´is_list´, ´$1´} (´$ 1´只是一个例子,编号可能有所不同)。试图调用一个不是匹配规格内置的函数将会导致错误。

  • 在高阶函数头部声明的变量,将被顺序性的替换为匹配规格变量,所以片段 fun({A,B,C}) 将被替换为 {´$1´, ´$2´, ´$3´} 。所有后续对这些变量的引用,都将以同样的方式被替换为匹配规格变量,所以高阶函数 fun({A,B}) when is_atom(A) -> B end 将被翻译为 [{{´$1´,´$2´},[{is_atom,´$1´}],[´$2´]}]。

  • 不是在高阶函数头部声明的变量,将从环境中导入并作为匹配规格的常量表达式。例如,从shell:

    1> X = 25.
    25
    2> ets:fun2ms(fun({A,B}) when A > X -> B end).
    [{{´$1´,´$2´},[{´>´,´$1´,{const,25}}],[´$2´]}]
    
  • 不能使用 = 匹配,不论是在高阶函数头部还是主体。再以shell为例:

    1> ets:fun2ms(fun({A,[B|C]=D}) when A > X -> B end).
    Error: fun with head matching (´=´ in head) cannot be translated into
    match_spec
    {error,transform_error}
    2> ets:fun2ms(fun({A,[B|C]}) when A > X -> D = [B|C], D end).
    Error: fun with body matching (´=´ in body) is illegal as match_spec
    {error,transform_error}
    

    所有变量都在头部被绑定并且匹配规格不允许多重绑定。

  • 匹配规格的特殊变量 ´$_´ 和 ´$*´ 通过伪函数 object()(与 ´$_´ 对应)和 bindings()(与 ´$*´ 对应)实现。举一个例子,我们可以翻译下面的 ets:match_object/2 调用为 ets:select 调用:

    ets:match_object(Table, {´$1´,test,´$2´}).
    

    等同于

    ets:select(Table, ets:fun2ms(fun({A,test,B}) -> object() end)).
    

    这只是一个例子,在这个简单的情况下,前一种表达方式可读性更好。上面的 ets:select/2 调用在概念上看起来像以下结果代码:

    ets:select(Table, [{{´$1´,test,´$2´},[],[´$_´]}]).
    
  • 术语(term)和文本会被尽可能的转换为有效的匹配规格,所以元组被翻译成匹配规格中的元组结构(一个包含一个元素的元组),常量表达式被用于从环境中引入变量的时候。记录被翻译成平坦的元组结构来调用内部的元素等。保护式测试函数 is_record/2 被翻译为使用三个参数的匹配规格那只函数调用,因此 is_record(A,t) 会被翻译成给定记录尺寸为5的 {is_record, ´$1´, t, 5} 调用。记录当然还是无法从shell访问...

  • 不能使用那些在匹配规格中无法表达的语言结构,例如:case、if、catch 等。

  • 不能在保护式(guard)中使用旧的类型测试函数名(list, integer, float 等) 。所有保护式必须使用新名is_list、is_tuple、is_record 等。

  • 如果没有包含头文件 ms_transform.hrl,高阶函数将不会被翻译成匹配规格,这可能会导致运行时错误,也就是说错误可能不会在编译时被发现,这取决于高阶函数是否在纯erlang的有效范围内。必须确保在使用 ets:fun2ms/1 和 dbg:fun2ms/1 时,头文件有正确保护。

  • 如果触发翻译的伪函数是 ets:fun2ms/1,那么高阶函数的头部必须至少包含一个变量或一个元组。如果是 dbg:fun2ms/1,那边高阶函数的头部必须至少包含一个变量或一个列表。

从高阶函数翻译成匹配规格是在编译时完成的,所以运行时性能不会受这些伪函数的影响。不过编译时间可能会多花费一些时间。

如需有关匹配规格(match_spec)的信息,请阅读erts users guide。

导出函数

parse_transform(Forms,_Options) -> Forms

类型:
Forms = Erlang 抽象代码格式, 请参阅 erl_parse 模块介绍
_Options = 选项列表, 必须但是不会被使用

在编译时实现真正的转换工作。当 ms_transform.hrl 头文件被包含在源代码中的时候,这个函数将被编译器调用以翻译源代码。查看 ets:fun2ms/1 和 dbg:fun2ms/1 函数的手册中关于如何使用 parse_transform 的说明,以及 ERTS users guide 中 match_spec 章节对匹配规格的描述。

transform_from_shell(Dialect,Clauses,BoundEnvironment) -> term()

类型:
Dialect = ets | dbg
Clauses = 单一的高阶函数(fun)的 Erlang 抽象形式
BoundEnvironment = [{atom(), term()}, ...], shell环境中的变量绑定列表

当fun2ms函数从shell中调用的时候,本函数实现真正的“转换时”。在这种情况下,抽象形式是一个单一的高阶函数(由erlang shell解析),所有已绑定的变量将通过 BoundEnvironment 以 key-value 列表的形式传递进来。返回结果是一个术语(term),普通的那种,不是抽象格式。

format_error(Errcode) -> ErrMessage

类型:
Errcode = term()
ErrMessage = string()

根据模块中其它函数中返回的错误代码返回一个错误的文字说明。其实是一个相当无趣的函数。