Сопоставление с образцом - реализация

Мне интересно, как обычно реализуется сопоставление с образцом. например, вы думаете, что в Erlang он реализован на уровне байт-кода (для этого есть байт-код, чтобы он выполнялся эффективно) или он генерируется компилятором как последовательность инструкций (серия байт-кодов)?

Это настолько полезная вещь, что мне просто нужно перевести ее на язык игрушек, который я создаю.

Ответов (4)

Решение

Вы можете увидеть, что произойдет, если скомпилировать какой-нибудь код

-module(match).
-export([match/1]).
match(X) -> {a,Y} = X.

Когда вы хотите увидеть, как выглядит ядро

> c(match, to_core).

или

$ erlc +to_core match.erl

результат

module 'match' ['match'/1,
                'module_info'/0,
                'module_info'/1]
    attributes []
'match'/1 =
    %% Line 3
    fun (_cor0) ->
        case _cor0 of
          <{'a',Y}> when 'true' ->
              _cor0
          ( <_cor1> when 'true' ->
                primop 'match_fail'
                    ({'badmatch',_cor1})
            -| ['compiler_generated'] )
        end
'module_info'/0 =
    fun () ->
        call 'erlang':'get_module_info'
            ('match')
'module_info'/1 =
    fun (_cor0) ->
        call 'erlang':'get_module_info'
            ('match', _cor0)

Если вы хотите увидеть asm-код луча, вы можете сделать

> c(match, 'S').

или

$ erlc -S match.erl

и результат

{module, match}.  %% version = 0

{exports, [{match,1},{module_info,0},{module_info,1}]}.

{attributes, []}.

{labels, 8}.


{function, match, 1, 2}.
  {label,1}.
    {func_info,{atom,match},{atom,match},1}.
  {label,2}.
    {test,is_tuple,{f,3},[{x,0}]}.
    {test,test_arity,{f,3},[{x,0},2]}.
    {get_tuple_element,{x,0},0,{x,1}}.
    {test,is_eq_exact,{f,3},[{x,1},{atom,a}]}.
    return.
  {label,3}.
    {badmatch,{x,0}}.


{function, module_info, 0, 5}.
  {label,4}.
    {func_info,{atom,match},{atom,module_info},0}.
  {label,5}.
    {move,{atom,match},{x,0}}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 7}.
  {label,6}.
    {func_info,{atom,match},{atom,module_info},1}.
  {label,7}.
    {move,{x,0},{x,1}}.
    {move,{atom,match},{x,0}}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

As you can see {test,is_tuple,..., {test,test_arity,..., {get_tuple_element,... and {test,is_eq_exact,... are instruction how this match is performed in beam and it's transformed directly to byte-code of beam.

Erlang compiler is implemented in Erlang itself and you can look at each phase of compilation in source code of compile module and details in depend modules.

Если вы хотите создать свой собственный сопоставитель шаблонов, есть статья Скотта и Рэмси и статья Люка Марангета, в которых описывается, как компилировать шаблоны в эффективные деревья решений (также известные как вложенные операторы переключения).

Лучшее, что я могу предложить, - это скомпилировать несколько тестовых функций и взглянуть на сгенерированный код.

erlc -S test.erl

генерирует test.S, который хорошо читается.

Чтобы ответить на этот вопрос, сопоставления с образцом эффективно создаются из более примитивных операций. Вот часть кода из предложения функции, соответствующего {X, [H | T]}.

{test,is_tuple,{f,1},[{x,0}]}.
{test,test_arity,{f,1},[{x,0},2]}.
{get_tuple_element,{x,0},0,{x,1}}.
{get_tuple_element,{x,0},1,{x,2}}.
{test,is_nonempty_list,{f,4},[{x,2}]}.

Очень хорошее описание компиляции сопоставления с образцом дано в «Реализации функциональных языков программирования» Саймона Пейтона Джонса . Это немного старая, но очень хорошая книга. Он также содержит, среди прочего, описание составления списков.

Компилятор Erlang использует оба алгоритма из книги.