Как стать автором
Обновить

Комментарии 69

Здорово. Плюсанул, можете перенести в блог «Erlang/OTP».
Очень интересно, но многое непонятно.
Как быть с константами? Или объявлять глобальную переменную? Можно ли получить к ней доступ или объявить ее из функции?
Через макросы (а-ля #define в си).
в качестве констант в Эрланге активно используются атомы

если же нужно именно глобально доступное именованое значение или макрос тогда можно использовать директиву препроцессора -define(name, substitution).

глобальных переменных не существует, так как не существует переменных вообще.

в последней версии Эрланга появилась недокументированная возможность создавать независимые екземпляры модулей с глобально доступными в каждой функции переменными, значения которых задаются при инициализации, детально почитать можно здесь: www.erlang.se/workshop/2003/paper/p29-carlsson.pdf
А можно ли атомы воспринимать как константы в обычном их понимании? Я думал насчет этого, и не кажется, что нет. С атомом вообще не связанно какое- либо значение кроме его литерального представления. Вернуть число атом не может.
вернуть число не может, но ведь не всегда нужны константы со значением? то есть почти всегда имя константы несет в себе смысл кода который вы пишете, значение же неважно и часто его и не знают. пример — перечисляемые типы данных.
Да. Но. К примеру, в константу можно загнать числовое значение которое использовать в математических выражениях. К примеру величину какого либо дискретного шага при вычислении функции (в математическом понимании этого слова).
В эрланге нет констант подобных константам в других языках. Я бы даже сказал, что по сути переменные как раз и являются константами для текущей зоны видимости, т.к. значение им может быть присвоено только один раз.

Я вообще не уверен, что для эрланга можно говорить о глобальной зоне видимости переменных. Работа с данными идет за счет того, что для обработки происходит рекурсивный вызов какой либо функции и в ходе каждой итерации мы получаем новую зону видимости и в ней уже новую переменную значение которой для текущей итерации изменено быть не может.
Тут чисто практический момент: например мне надо где-то указать адрес сервера, куда надо подключаться. Удобнее всего это вынести в некоторую переменную/константу в глобальной области видимости, чтобы везде где надо легко ее использовать.
Как это сделать на Эрланге?
Также как и в си, делаем заголовочный файл, а потом его включаем.

в server_info.hrl:

-define(SERVER_PORT, 8080).

в server_connect.erl:

-include(«server_info.hrl»).
Ага, спасибо!

Еще чайниковый вопрос: чем отличается файл .erl от .hrl?
По какой модели работает include: как в PHP — то есть вложение файла в точку вызова, или как в Python — код подклчается как объект со своим пространством имен? То есть есть ли в Эрланге пространства имен и как с ними работать? Можно модули друг в друга вкладывать?
Вызывать примерно так:
package:module:submodule:method()?
Вкладывать модули друг в друга нельзя, а -include работает так же как в си (и наверное пхп) — то есть эквивалентно тупой вставке текста из .hrl

Соответственно, .hrl это текстовый файл просто, он никак компилятором специально не обрабатывается, а в .erl всегда описание модуля.
Так Irr выше уже писал. Через макросы.
В смысле, что выше с самого начала (3 пост), поэтому я и не стал про это упоминать.
Есть еще файл приложения .app, там можно записывать всякие конфигурационные параметры, которые потом можно узнавать из кода программы в процессе выполнения. То есть получается некий аналог ini файла. Преимущество с макросами — не требует перекомпиляции кода.
Не совсем понял на счет не требует перекомпиляции — т.е. в работающем приложении достаточно в .hrl файле подредактировать макрос и новое значение автоматом подхватится?
.hrl файл требует перекомпиляции, а я пишу про .app файл. Я не точно сформулировал, правильнее так: «Преимущества .app файла по сравнению с макросами — не требует перекомпиляции код».
Спасибо. Никак руки не доходят прочитать официальные мануалы по ерлангу. А так по кусочкам глядишь и познаю.
Я боюсь, что для тех, кто об Эрланге ни чего не знает данная статья не лучший материал. Она обзорная по сути, но делает не обзор возможностей языка. Нет ни примеров программ, ни описания количества вычислительных ресурсов необходимых программе, т.е. нет тех вещей который абсолютному новичку могли бы показать, что написание программы на эрланге может дать какие либо ему преимущества (большая скорость разработки, меньшие аппаратные требования, более высокая стабильность, прочее) по сравнению с тем языком, который он уже знает.

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

Поэтому абсолютному новичку видимо стоит все же начать с «Начала работы с Erlang» ссылка на которую приведена в конце (может конечно её стоило поместить в начало, но я как то по привычке ссылку на связанные ресурсы решил дать в конце), тем более, что и сама документация начинается по сути именно с этого материала.
У меня как раз ситуация из 2го абзаца. Я уже знаком с ерлангом и даже модифицировал кое-какие модули ejabberdа. Но при этом нормальной программы с нуля написать не смогу, потому что не изучил основ, подобных описанным в статье. Поэтому для меня она и полезна :)
Запись на этапе компиляции преобразуется в кортеж, поэтому использовать записи напрямую в шелле невозможно.

Тут бы лучше про функцию rr() рассказать, потому что описания рекордов чаще всего выносятся в отдельный файл, и из шелла rr() самое то, чтобы их использовать.
Возможно. Но цель описать типы и методы их создания, не описание функций шелла. Поэтому вопрос использования функции вторичен, имхо. Главное, что все примеры можно набрать в шелле и поэкспериментировать с ними без каких либо дополнительных телодвижений.

Да и к тому же я не так много работал с BIF-ами, что бы задумываться о таких вещах :D
Функции же просто можно писать:

Func(Pattern) when Guard ->
....;
Func(Pattern2) ->
....;
Func(PatternN) ->
….
Это не функция. Это функция-объект, такой тип элементарных данных. Напоминает Javascript.

Если честно, я не знаю, почему создатели языка выделили такой тип, но по всей видимости были причины иметь среди базовых типов и такой.
я понял. просто анонимная функция, и как в других функциональных языках её можно присвоить переменной :)
Относительно её я не стал использовать термин «анонимная функция» именно с тем, что бы не путать лишний раз. Сказали создатели языка, что это отдельный тип, значит так оно и есть :D
Обычные функции можно тоже получить в виде хранимого значения, не только анонимные:

> F = fun lists:map/2.
#Fun<lists.map.2>
Разве? Мне кажется это пример анонимной функции в которую вложена обычная.
Тогда между угловыми скобками были бы всякие страшные числа, как в примере в статье. Сравните с fun(F, L) -> lists:map(F, L) end.
Очень хочется увидеть типичную задачу для ерланга и ее реализацию. Что-то вроде простенького работающего кода, делающего что-то полезное. Иначе не ясно для чего весь этот огород.
Да, именно это и планируется. Собственно сама идея статьи с этого и начиналась. Точнее с вопроса о работе с бинарными данными. И это будет в завершающей, третьей части. Я так решил сделать что бы понятно, что происходит в каждой строке кода и почему. К примеру смотря простые примеры программ на Эрланге многие думаю, что "=" оператор присвоения. Перменная Х равна 5, к примеру. Хотя на самом деле это оператор сопоставления. И разница между ними даже в самом методе использования.

А пока этого нет, можно для примера прочесть «Начала работы с Erlang».
А можно чуть подробнее про работу со строками и бинарными данными. Я смотрел: в некоторых проектах используются бинарные строки как обчные для хранения текста.
КРоме того, вы пишете, что строк как таковых нет. Это, честно, сильно напрягло. Например, мне надо сделать строку с русским текстом, В кодировке UTF-8 это будет 2 байта на один символ. И тут может возникнуть проблема. Только что опробовано в консоли:

(erl@laptop)3> <<"ABC">>.
<<"ABC">>
(erl@laptop)4>
<<"АБВ">>.
<<208,144,208,145,208,146>>

Латинская строка обработалась нормально, а наша разбилась на отдельные байты. Причем даже задать длину данных нельзя — т.к. в UTF-8 разные символы имеют разную длину.
ТАк же непонятно как оперировать со строками: разбить/склеить, заменить слова и т.п. Ведь если нет строк, то нет и операций с ними?

Спасибо за объяснение.
Строка это последовательность байт.

С utf8 вы огребаете ровно тоже, что и в си. Влина строке не равна колличеству байт в ней.

В чем разница/проблема?
Наверное, я избалован Питоном, где есть специальный объект unicode для таких строк.
Поскольку он часто используется в пмоих прооектах, то я хочу понять как здесь его заменить.
Нет, в эрланге нету специального объекта для юникода, и это иногда не очень удобно (по сравнению с питоном). Эрланг вообще очень минималистичный такой язык, авторы его специально делали попроще, чтоб порог вхождения снизить как можно сильнее.

По мне, кстати, идеально было бы иметь удобный способ интеграции его с питоном (все хочу сам как-нибудь написать, но времени не хватает) — и на эрланге делать всю распределенную/управляющую часть, а на питоне всякую сложную обработку данных, куча полезных питонских библиотек очень пригодилась бы тут.
Я бы сказал, что отсутствие поддержки юникода не упрощает, а наоборот усложняет язык. Некоторые проекты на Эрланге работают в юникоде — значит эту проблему пришлось решать «вручную».
Если бы можно было подключать питоновские либы, это был бы большой шаг вперед.
В любом языке строки это не более, чем двоичные последовательности. И то, что мы видим символы это заслуга шела/терминала другого ПО визуального отображения. Просто все эти преобразования давно уже написано до нас и находяться глубоко в недрах системы.

В языках, которые развивают активно и давно уже понаписано куча софта которая ведет редактирование строк. А зачастую это включается и базовую поставку. Поэтому все операции обработки строк скрыты внутри этих библиотек. Но работа ведется все равно с двоичными данными.

В Эрланге этого еще нет. Нет к примеру встроеных библиотек для работы с юникодом. И я сильно подозреваю, что написано это если и будет, то очень не скоро, т.к. виртуальная машина вполне хорошо может взаимодейтствовать с внешним ПО и в этом контексте гораздо разумнее воспользовать уже готовым, проверенным решением на том же С, чем городить новый огород который первые несколько лет будет бальшим багодромом.

Насчет разной длины это да, но ведь у UTF-8 есть одна очень удобная особенность. В первом байте в двух старших разрядах в нем установлены единицы (т.е. первый байт имеет вид 11xxxxxx), а все последующие байты в старших разрядах имеют единицу и ноль (т.е. 10xxxxxx). Поскольку реально используется только 4 байта, а так же в начале файла можеть быть еще 3 байта BOM-а (EF BB BF), то нужно всего лишь перед работой прочесть первых 7 байт и по их структуре будет видна размерной символа в байтах для данной бинарной строки.

В VM идет работа только в однойбайтовой кодировкой. Мне не очень понятно, как у тебя получилось получить данные пример. У меня получается как и положено 3 байта:
1> B = <<"АБВ">>,
1> bit_size(B).
24
Юникод кстати обещали в следующем релизе (R12B-6 что ли). Ну то есть не то чтобы все будет сразу шоколадно, с библиотеками и всеми остальными делами, но будет, как минимум, унифицированный способ представления уникодных строк (UTF-32 как список, UTF-8 как binary).
Да, приходилось об этом уже слышать.

А почему UTF-32 как список? Что-то не уловил %(
Ну со строкой как списком вообще работать довольно удобно в функциональном стиле, в плане маттерн матчинга и прочих штук, и поскольку числа в эрланге неограниченной разрядности, UTF-32 пихаем в список и нет проблем.

А UTF-8 однозначно в виде списка трудно представить (если побайтно то вся прелесть теряется), поэтому его лучше как binary держать, плюс вроде предлагалось добавить поддержку в binary паттернах юникодных строк (типа, <<u«Превед»>>).
Понятно, что внутри нет никаких строк, даже чисел и то нет — есть только последовательности байтов. Вопрос только в том, насколько удобно работать с этими данными. В конечном-то счете есть только один язык программирования — машинный код, а все остальные языки придуманы, чтобы упростить работу программисту, избавить его от совсем тупой рутины типа работы с регистрами и подсчета адресов условных и безусловных переходов. Языки отличаются только тем, насколько они упрощают эту задачу и помогают сократить рутину. А заодно абстрагироваться от конкретной железяки. В этом плане сильная привязка к железу меня смутила.
А здесь получается так: если нет стандартной библиотеки, то придется писать самому — тут-то и выйдет багодром. Когда сотня человек будет писать одно и тоже в своих проектах. Гораздо удобнее, когда есть готовая протестированная либа.

Не очень понял насчет использования готовых либ из С? Их можно как-то подключать?

У меня стабильно выходит 6 байт:
(erl@laptop)6> B = <<"АБВ">>.
<<208,144,208,145,208,146>>
(erl@laptop)7> bit_size(B).
48


Проверено на Erl-5.6.3 на Ubuntu 8.10 и на Erl-5.6.5 на WinXP. Я думал это из-за юникодовой локали, но на винде тот же эффект. Может какие-то настройки неверные?
Можно, но я лично не подключал, пока такой надобности еще не возникало. vostryakov может прокоментирует, он точно подключал как минимум либу для обработки UTF, и на сколько я даже помню где-то в каментах обещал рассмотреть этот вопрос.

Насчет 6 байт прямо даже интересно… у меня на WinXP Pro&SP2 Ru стоит VM 5.6.5 и я вижу положенных 3 байта. Все же наверное что-то в система так настроено, ведь поддежки UTF-а я VM на данный момент нет еще точно.

А у кого еще 6 байт выходит?
Дык там не надо со стороны VM, достаточно чтоб парсер у компилятора это умел. У меня на убунте тоже 6 :)
А они у вас в консоли нормально вводятся?
У меня как-то слишком весело... :)
У меня так же :)
То есть консоль это сразу преобразует в байтовую последовательность?
Только я не могу понять, что такое \320?

О! Да это вообще восьмеричная последовательность!
В utf-8 русская А кодируется как d0 90 — что как раз и будет 0320 0220.
Ну да, консоль там юникод не умеет :(
А вы как ставили эрланг?
Может причина в ИДЕ — я запускаю и играюсь в Erlide — видимо она и подменяет настройки.
А в консоли на убунту совсем весело:

Erlang (BEAM) emulator version 5.6.3 [source] [smp:2] [async-threads:0] [kernel-poll:false]

Eshell V5.6.3 (abort with ^G)
1> A = <<"\320\220\320\221\320\222">>.
<<208,144,208,145,208,146>>


Обратите внимание — что появилось при наборе. На клавиатуре набирал русские АБВ!
Локаль в полном порядке — и шелл, и все остальные программы (включая интрпретатор питона нормально работают с русскими буквами).
Только что написал небольшой модулек, проверить как там будет с русским:
$ cat a.erl
-module(a).

-export([hello/0]).

hello() ->
A = "Test по-русски",
io:format("~p ~n", [A]).


Запускаем:

$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [smp:2] [async-threads:0] [kernel-poll:false]

Eshell V5.6.3 (abort with ^G)
1> c(a).
{ok,a}
2> a:hello().
[84,101,115,116,32,208,191,208,190,45,209,128,209,131,209,
129,209,129,208,186,208,184]
ok
3>


P.S. А куда делся пробел и ~n?
Пробел и ~n никуда не делись, вон они :)

io:format это аналог print, а если нужен sprintf то надо использовать io_lib:format().
В конце массива должно быть 32, 10, а может даже и 13, а он заканчивается 208,184 — русской буквой В.
Ну, правильно, в питоне этот код эквивалентен
print "%s \n" % (repr(A)),

Т.е. массив этот — это строковое представление A, а пробел и \n идут как есть в stdout.
Теперь понял — я думал, что это на этапе вывода строка преобразуется в список чисел, и недоумевал почему внутренний пробел сконвертировался, в конечный нет. Если преобразование происходит чуть раньше на стадии форматирования строки, то все верно — именно такой результат и должен быть.
Спасибо!
Меня сбило с толку указание во многих мануалах, что интерпретатор достаточно «интеллектуальный» в части вывода строк на печать :)
Не все так ужасно, на самом деле, есть сторонние библиотеки для юникода, и даже в стандартных библиотеках можно найти функции преобразующие utf-8 binary в список unicode code points, дальше с ним можно нормально работать уже как с обычным списком. Особенных велосипедов не требуется изобретать. Хотя, конечно, хотелось бы чуть более серьезную поддержку юникода «из коробки», и вроде как это скоро обещают.

Сишные либы, конечно, можно прикрутить, правда пока нету для этого таких удобных инструментов, как для питона.
Что такое «unicode code points»?
Само собой встроенная поддержка была бы очень к месту.
Ну у каждого символа в юникоде есть номер, логически, без ограничения на разрядность, он называется code point.

А для того чтобы эти номера хранить в памяти, они каким-то образом кодируются (UTF-8, UTF-16, UTF-32..) в бинарную форму. В UTF-32 вот тупо номер code point'а записывается как 32-битный unsigned int, например.
Чуть ниже (в ответе irr) я пояснил несколько моментов про строки в Эрланге и UTF.
Давайте я попорядку проясню вопрос с UTF и простыми строками в Эрланге:
1. Строк, как отдельного типа данных нету. В Эрланге — это список. Но! Есть бибилиотека для работы со строками: string, которая содержит все необходимые функции для работы со строками. Но все это работает только с английскими символами.
2. Преобразование в UTF-8, UTF-16, UTF-32 и обратно в обычный ASCII в Эрланге уже есть, но нет библиотеки для работы с полученной строкой UTF
3. Поддержку UTF обещают в следующей версии R13B которая выходит в марте. То есть уже вот-вот и по моему сделать им это будет не сложно.
4. Пока мы используем библиотеку starling для работы с UTF, которая написана на C, но имеет интерфейс для прямого вызова из Эрланговских программ. Это не чудо, Эрланг позволяет вызывать код, написанный на другом языке, при написании специального драйвера связывающего программу на Эрланге и другом языке.
Не, на самом деле там есть функции в xmerl (только они в документации не описаны ;) которые utf-8/16/32 что угодно парсят в список юникодных символов и обратно, и дальше с ним можно работать как с обычными строками.
Т.е. «как с обычными списками».
Да, о чем я и пишу в пункте 2. Только вот string:to_upper() над ними выполнять смысла большого нет. То есть не получиться с ними работать, как с обычными ASCII строками.
Спасибо за подробный ответ!
Можно несколько вопросов?
1. Не возникает ли из-за этого дикий оверхед в части хранения и обработки таких строк? Я правильно понимаю, что список — это набор пар {значение, указатель на следующий элемент}?
4. Есть ли какие-то проблемы с этой библиотекой? Хватает ли функционала?
Я отвечу на вопрос 1: возникает, это 8 байт на символ, поэтому очень большие строки лучше как binary держать (но с другой стороны, с большими строками редко надо производить какие-то нетривиальные строковые операции).

Ко второму вопросу присоединяюсь :)
Второй это который? Если о списке, то конечно же да. В VM он же является банальным однонаправленым связанным линейным списком со всеми вытекающими отсюда.
Функционала нам лично хватает. Найдите в гугле описание этой библиотеки по словам Starling + Erlang. В общем там по моему все обычные функции для работы со строками. Что собственно с ней плохо — это скорость работы. За счет постоянных вызовом этой сторонней библиотеки, скорость достаточно мала на мой взгляд. Я не помню точных цифр, но примерно не более 100 тыс. операции upper_case в секунду. Поэтому и жду поддержку unicode в самом Эрланге, надеюсь на сильное повышение скорости работы :)
Я уже нашел: 12monkeys.co.uk/starling/ Оно?
Спасибо. Будем ждать :)
Да, оно :)
> то на символ уходит 8 или 16 байт (2 машинных слова)

может 8 или 16 бит?
Нет, байт.

>String (is the same as a list of integers): 2 words per character

На 32-ой архитектуре машинное слово равно 4 байтам, значит 2 машинных слова будет 8 байт.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации