Вы, вероятно, не заметили, но прошло пару недель с тех пор, как я в последний раз публиковал блог. Это объясняется тем, что я сломал Солей, и моя нога в гипсе. Будучи неподвижным, я подумал, что было бы неплохо исследовать что-то совершенно другое — это было либо это, либо просмотр дневного телевидения, и, хотя повторы Коджака и Магнума Пи были заманчивыми, расследование Эрланга вышло на вершину.
Важно помнить, что это не
В руководстве Erlang идея заключается в том, чтобы изучить некоторые сходства между Erlang и Java, чтобы попытаться предоставить отправную точку для изучения Erlang. Если я допустил какие-то вопиющие ошибки, то, надеюсь, кто-то с большим опытом работы с Эрлангом даст мне знать.

Что я имею в виду под этим? В Java код хранится в файлах, которые представляют классы, а класс представляет собой группу данных и методы, которые выполняют единственную ответственность. Вы создаете экземпляр класса и обращаетесь к его методам через переменную или, вы можете обращаться к его статическим методам через имя класса.
В Erlang код хранится в файлах, называемых модулями, причем каждый модуль представляет собой группу функций, которые выполняют одну ответственность. Вы не можете создать экземпляр модуля, а переменные экземпляра и переменные класса не существуют; Вы можете использовать только переменные аргумента метода. Вы получаете доступ к методу через имя его модуля аналогично доступу к статическому методу Java. Как и классы Java, модули Erlang имеют частные и публичные функции. 
Будучи разработчиком Java, я был рад обнаружить, что есть
Erlang плагин для затмения . Это потому, что быстрее выучить только язык, а не язык и совершенно новый набор инструментов разработки. Несколько консультантов Erlang, с которыми я разговаривал несколько месяцев назад, сказали, что они предпочитают использовать emacs, и я удивлялся, почему, пока не обнаружил, что плагин eclipse довольно ненадежный. Тем не менее, это достаточно хорошо, чтобы начать, и у него есть потенциал.
Есть и другие сходства, которые должны заставить любого разработчика Java чувствовать себя как дома: исходные файлы модуля Erlang компилируются в файлы .beam которые затем запускаются на виртуальной машине Erlang; есть также eunit , эквивалент Erlang для JUnit , и log4erl , который, как следует из его названия, является версией Erlang Log4J. Существует автоматическая генерация документов с использованием edoc , версия Javadoc Эрланга и стандартная компоновка проекта, которая очень похожа на компоновку Maven, которая выглядит следующим образом:

Структура немного отличается от Maven: целевой каталог называется ebin, а каталоги src и test разделены на уровне каталога проекта, но за ним легко следовать, и вы к нему привыкли.
Из того, что я сказал до сих пор, можно подумать, что самое большое различие между Java и Erlang заключается в том, что файлы Java имеют расширение .java а файлы Erlang имеют расширение .erl . К сожалению, в этом есть нечто большее, во-первых, это маленький вопрос или странный синтаксис Эрланга 1 .
Чтобы исследовать это, я подумал, что я возьму свои существующие классы ShoppingCart и ShoppingCartTest и переведу их на Erlang. Эти два класса доступны в моем проекте telldontask выглядят примерно так …
Класс ShoppingCart
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
public class ShoppingCart { private final List<Item> items; private PaymentMethod method; public ShoppingCart() { items = new ArrayList<Item>(); } public void addItem(Item item) { items.add(item); } public double calcTotalCost() { double total = 0.0; for (Item item : items) { total += item.getPrice(); } return total; } public double calcTotalCost(double shipping, double minShippingAmount) { double totalCost = calcTotalCost(); if (totalCost > minShippingAmount) { totalCost += shipping; } return totalCost; } public void setPaymentMethod(PaymentMethod method) { this.method = method; } public void pay(double shipping, double minShippingAmount) { double totalCost = calcTotalCost(shipping, minShippingAmount); method.pay(totalCost); } } |
ShoppingCartTest JUnit
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class ShoppingCartTest { /** * Test method for {@link tell_dont_ask.ask.ShoppingCart#getAllItems()}. */ @Test public void calculateTotalCost() { ShoppingCart instance = new ShoppingCart(); Item a = new Item("gloves", 23.43); instance.addItem(a); Item b = new Item("hat", 10.99); instance.addItem(b); Item c = new Item("scarf", 5.99); instance.addItem(c); double totalCost = instance.calcTotalCost(); assertEquals(40.41, totalCost, 0.0001); } } |
Приведенный выше код демонстрирует некоторые базовые функции корзины покупок; однако, для более подробной информации о том, как работают эти классы, взгляните на Определение Сказать Не спрашивать и Разборка Сказать Не спрашивать .
Эквивалентный код на Erlang выглядит примерно так:
Модуль shopping_cart
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
-module(shopping_cart). %% ==================================================================== %% API functions %% ==================================================================== -export([add_item/2,calc_total_cost/1,calc_total_cost/3,pay/3]). %% @doc Add an item to the order list add_item(OrderList,Item) -> [Item | OrderList]. %% @doc Calculate the total cost of all the items in a list. The List must have the following format: %% [{itemName, Price}] %% where %% itemName -> atom %% Price -> float() calc_total_cost(OrderList) -> round_dp(calc_total_cost(0,OrderList)). %% @doc Calculate the total cost of all the items in a list adding a shipping cost if the value is below a certain limit. %% The Order List must have the following format: %% [{itemName, Price}] %% where %% itemName -> atom %% Price -> float() calc_total_cost(OrderList,Shipping, MinShippingAmount) -> Cost = calc_total_cost(OrderList), TotalCost = Cost + shipping(Cost,Shipping,MinShippingAmount), round_dp(TotalCost). %% @doc @todo Method not implemented pay(_Order,_Shipping, _MinShippingAmount) -> unimplemented. %% ==================================================================== %% Internal functions %% ==================================================================== calc_total_cost(Result,[{_,Price} | TheRest]) -> calc_total_cost(Result + Price,TheRest); calc_total_cost(Result,[]) -> Result. shipping(Cost,Shipping,MinShippingAmount) when Cost < MinShippingAmount -> Shipping; shipping(_,_,_) -> 0. round_dp(Number) -> List = float_to_list(Number,[{decimals,2}]), list_to_float(List). |
Модуль shopping_cart_tests
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
-include_lib("eunit/include/eunit.hrl"). -module(shopping_cart_tests). %% ==================================================================== %% API functions %% ==================================================================== -export([]). %% @doc Calculate total code - written to match the Java style calculate_total_cost_test() -> EmptyList = [], OrderList1 = shopping_cart:add_item(EmptyList,{gloves,23.43}), OrderList2 = shopping_cart:add_item(OrderList1,{hat,10.99}), OrderList3 = shopping_cart:add_item(OrderList2,{scarf,5.99}), ?_assertEqual(40.42,shopping_cart:calc_total_cost(OrderList3)). %% @doc Calculate total cost example - written in a better erlang style calculate_total_cost_2_test() -> OrderList = [{gloves,23.43},{hat,10.99},{scarf,5.99}], ?assertEqual(40.41,shopping_cart:calc_total_cost(OrderList)). |
Эксперты говорят мне, что с Erlang вы можете сделать гораздо больше за меньшее количество строк кода. Это не тот случай, но с другой стороны, я добавил много строк комментариев (обозначается разделителем «%»).
По сравнению с Java приведенный выше код выглядит довольно странно (он также выглядит немного уродливо, так как у меня нет конвертера Erlang в HTML). Переменных экземпляра нет, поэтому любые необходимые данные передаются в качестве аргументов функции. Если вы посмотрите на add_item(…) то увидите, что он добавляет переменную Item в начало списка, например: items.add(item) (обратите внимание, что в именах переменных Erlang всегда начинаются с заглавной буквы).
Переход к calc_total_cost() и все начинает выглядеть очень странно… calc_total_cost(OrderList) — это просто оболочка для calc_total_cost(0,OrderList) . calc_total_cost(0,OrderList) — это вызов либо calc_total_cost(Result,[{_,Price} | TheRest]) либо calc_total_cost(Result,[]) , которые являются функциями, которые выполняют циклическое добавление цен на товары из списка. , За исключением того, что он не зацикливается; В Erlang нет циклов for , вам нужно использовать рекурсию, постепенно добавляя цены в calc_total_cost(Result,[{_,Price} | TheRest]) а затем рекурсивно вызывать себя, пока список не станет пустым.
Суть синтаксиса Erlang в том, что, хотя разработчик Java привык к языкам, являющимся производными от C , он очень логичен и поэтому его легко подобрать.
Стоит отметить, что вышеприведенный Erlang был написан для имитации Java. Вероятно, вы не подходите к разработке корзины покупок Erlang с нуля.
Почему вы выбрали бы Erlang вместо Java? Конечно, не за его сходство с Java. Вы бы выбрали Erlang, а не Java, когда его функции и преимущества помогут вам решить ваши проблемы более эффективно и с минимальными затратами. Согласно заявкам Ленарта Османа на Google Tech Talk Erlang, которые должны быть:
- отказоустойчивой
- без остановки
- параллельный
- распределенный, масштабируемый и разнородный
- мягкий в реальном времени
- требуют «прототипа»
Эти цели были достигнуты несколькими способами. Например, передача сообщений между процессами является частью языка, а не отдельным API. Чтобы отправить сообщение другому процессу, просто введите:
|
1
|
Pid ! theMessage, |
… Где Pid — это идентификатор процесса, который получит theMessage . Чтобы отправить сообщение другому процессу, запущенному на другой виртуальной машине Erlang, вы набираете:
|
1
|
Pid ! theMessage, |
Из этой несмешной шутки я мог догадаться, что процессы на виртуальных машинах Erlang прозрачны по расположению. Это означает, что процесс Erlang не отличается от того, на какой машине он работает; будь то локально или на другом физическом оборудовании. Это потому, что виртуальные машины Erlang могут общаться друг с другом и могут быть кластеризованы; что-то далеко за пределами JVM.
Чтобы получить сообщение, вы используете ключевое слово Erlang: receive , что-то вроде этого:
|
1
2
3
4
5
6
|
%% @doc Receive a message and print the contents print() -> receive Message -> io:format("The message is: ~p~n",[Message]) end. |
Сами процессы очень легковесны и предназначены для использования гиперпоточности на мегаядерных процессорах таким образом, что другие языки не могут имитировать. Процессы очень важны в Erlang, я где-то читал, что если Java является объектно-ориентированным языком, то Erlang является процессно-ориентированным языком.
Если этот блог читается как реклама для Erlang, то это потому, что после того, как я опустил голову в воду, я вижу, что есть ряд проблем, которые он может решить в моем текущем проекте дешевле и с меньшими хлопотами, чем решение на основе Java. , С другой стороны, существуют проблемы, для которых Java и Spring — обычная тема этого блога — лучше подходят. Компьютерные языки — это просто инструменты, и вы всегда должны выбирать лучший для этой работы.
1 Странно выглядит, если вы программист на Java, так как он основан на Прологе, а не на C.
Примеры кода Java для этого блога доступны в репозитории Captain Debug Github:
https://github.com/roghughe/captaindebug , хотя код Erlang доступен в моем репозитории Erlang Samples Github:
https://github.com/roghughe/erlang_samples .