неділя, 24 лютого 2013 р.

Как я на Erlang'е OOP делал

Написал в учебных целях утилиту для разбора build-output'а (Visual Studio/Incredibuild). Код тут.

Классно, что в Эрланге гарантируется очередность отправки/получения сообщений, то есть при определенных условиях можно гарантировать, что порядок вызова "методов" и порядок их обработки совпадут. Такой подход обеспечивает все плюсы модели Actor'ов (или MPI, как угодно) на уровне взаимодействия процессов, и плюсов функционального стиля на уровне реализации процессов.
Нужно также заметить (я сразу скептически отнесся к "еще одному динамически-типизированному языку"), что Эрланг намного статически типизирован (или менее динамически типизирован) чем, например, Python: он на этапе компиляции точно "знает", какие функции (наверное кроме анонимных) определены в компилируемом модуле, а это есть одно из необходимых условий оптимизации хвостовой рекурсии (далеко не во всех динамически-типизированных языках это возможно); также он делает проверку структуры (я бы не называл это проверкой типов), например компиляция кода:
...
A = atata,
ololo = A,
...
выдаст "Warning: no clause will ever match". То есть, Эрланг пробует унифицировать все, о чем есть информация на этапе компиляции. Правда делает он это только на уровне функции (но и это уже не мало). Такой код, например, компилируется без ошибки, хотя вызов f() будет с ошибкой:
...
f() -> g(ololo).
g(atata) -> void.
...
Кроме этого Эрланг проверяет определена ли используемая переменная, а также используется ли ранее определеная переменная. То есть код:
...
f() -> A = B.
...
во время компиляции выдаст:
Error: variable 'B' is unbound
Warning: variable 'A' is unused

Ну и конечно же ООП

Значит придумалось мне снабжать процесс функцией, которая "подготавливает" для него каждое сообщение, в частности как отправлять (синхронно/асинхронно) и отправлять ли вообще (оборачиваем функцией wrap_pid/2; пример create_base/0). В результате мой объект понимает "методы" status (синхронно), exit (асинхронно) и строковые константы (тоже асинхронно). Пример работы функция test/0.
Была также интересная у меня проблема, когда процессу отправлялись два сообщения одно за другим, причем первое это exit, а второе какое-то синхронное. В таком случае процесс-отправитель может заблокироваться, так как он будет ожидать ответ на второе сообщение. Решить это помог receive из timeout'ом (функция receive_alive/1).

4 коментарі:

  1. «он "знает" о всех функциях модуля, что позволяет оптимизировать хвостовую рекурсию» — чтобы оптимизировать хвостовую рекурсию, эрлангу достаточно посмотреть, что результат функции сразу возвращается вверх, что над ним не производится вычислений.

    ВідповістиВидалити
    Відповіді
    1. Это понятно, но все таки в динамически типизированных языках нужно еще и "знать" о функциях. В питоне например хвостовая рекурсия не оптимизируется, потому что нет гарантий, что вызываемая функция, это именно та, что нужно.

      Видалити
  2. Вы пишите странные вещи. То, что вы перечислили, конечно, соответствует истине и удобно, но, увы, никак не связано со статической типизацией.

    >> он "знает" о всех функциях модуля

    Попробуйте:

    external_module:no_such_function_bebebe(A,B,C)

    ВідповістиВидалити
    Відповіді
    1. "намного статически типизирован чем" не значит статически типизирован. Можете читать это как "менее динамически типизирован". А насчет "знает", я имел ввиду о функциях данного (который компилируется) модуля. Это наверное совсем незаметное требование для оптимизации хвостовой рекурсии: определять и вызывать функцию рекурсивно в ТОМ ЖЕ МОДУЛЕ, для того чтобы хвостовая рекурсия оптимизировалась.
      Как вы можете компилируя свой модуль добиться оптимизации хвостовой рекурсии этой внешней "external_module:no_such_function_bebebe(A,B,C)" функции?

      Видалити