дай ей умереть

183 views
Skip to first unread message

ufm

unread,
Jan 21, 2013, 9:00:28 AM1/21/13
to erlang-...@googlegroups.com
Hi!

Дискуссионный вопрос.

Мне кажется, что парадигму "дай ей умереть" некоторые программисты восприняли как "ура, ничего не проверяем". В результате имеем то, что я описал в топике про монгодб -
emongo:hex2dec("Test").
<<"Nt">>

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

Второй вопрос - как реагировать. Я придерживаюсь правила - "функция всегда должна возвращать осмысленные данные, которые можно передать как входные параметры в другую функцию".  Поэтому я считаю, что возврат {ok, Value} - это зло.

Я пишу примерно так:
someFunc(L) when is_list(L) ->
    haveSense
(L) orelse throw(nosense_param),
    someDo
(L)
.


Поругайте?

Max Lapshin

unread,
Jan 21, 2013, 9:03:36 AM1/21/13
to erlang-...@googlegroups.com
Это не зло, просто приходится выбирать: либо функция возвращает код ошибки, либо кидает исключение.

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

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

Возможно кто-нибудь расскажет другие соображения.


2013/1/21 ufm <ufm...@gmail.com>


--
--
Страница рассылки: http://groups.google.com/group/erlang-russian
Новости: http://erlanger.ru
Чат: xmpp://erl...@conference.jabber.ru
Чат для оффтопа: xmpp://erlang...@conference.jabber.ru
Правила, действующие в чате и рассылке: http://erlanger.ru/ru/erlang-at-conference-jabber-ru
 
Написать письмо: erlang-...@googlegroups.com
Отписаться: erlang-russia...@googlegroups.com
 
 

JLarky

unread,
Jan 21, 2013, 9:05:27 AM1/21/13
to erlang-...@googlegroups.com
замените throw на error
В остальном всё правильно. Просто когда слышал let it crash то
нечаянно путают это с вечным противостоянием:
крашить весь апликейшен по первому шороху "ибо так быстрее найти
ошибку" аля python-way
ловить все эксепшены и молча переходить к следующей инструкции "ибо
приложение должно всегда работать" аля java-way (сюда надо ещё как-то
добавить "интерпраиз" и "индус[трия]")

2013/1/21 ufm <ufm...@gmail.com>:

JLarky

unread,
Jan 21, 2013, 9:06:40 AM1/21/13
to erlang-...@googlegroups.com
> В остальном всё правильно. Просто когда слышал let it crash то
слышал -> слышат

JLarky

unread,
Jan 21, 2013, 9:15:31 AM1/21/13
to erlang-...@googlegroups.com
> код ошибки удобен для библиотечных функций, не всегда приятно, когда
> какая-то либа кидает эксепшны в коде.
> исключение удобно для бизнес-логики.
>
> Возможно кто-нибудь расскажет другие соображения.
всё верно, в golang например вообще исключений нет :) потому что им не
нужно эмулировать return через throw как в эрланге, там всё делается
через явную передачу кода ошибок потому что есть возможность
возвращать сразу несколько аргументов функции, что в эрланге делается
через {ok, Value} | {error, Error}

Вообще для меня let it crash это про то как надо прежде чем что-то
писать понять как можно исправлять ошибки на более низком уровне. Типа
у нас может упасть VM, должен ли bash скриптик её запустивший
перезапустить vm или проверить скажем что на сервере не наступил oom
(ram или fs) из-за каких-то третих сервисов, может быть послать
сообщение админу?
если упал аппликейшен в vm нужно его перезапустить? отсигналить?
перезапустить весь vm?
если упал воркер, нужно его перезапустить? отсигналить, перезапустить
весь supervisor tree?
если упала функция хендлер, то мы должны порвать связь с клиентом его
породившим, или вернуть ему 500? или просто проигнорировать? или
записать в логи попытку хакерской атаки?
если упал вызов 3rd party библиотеки мы должны считать что кривые
данные, кривая библиотека, кривой сервис к которой она обращается?
и да, когда на все эти вопросы есть ответ, то можно let it crash,
потому что всегда сверхну найдётся кто-то, кто решит нашу проблему с
той или иной потерей функциональности будь то падение конкретного
запроса или переподключение клиента или переподключение всех клиентов.

ufm

unread,
Jan 21, 2013, 9:35:13 AM1/21/13
to erlang-...@googlegroups.com
Для меня, честно говоря, смысл возвращения {error, Error} непонятен. Чем это отличается от эксепшена? Точнее - это отличается от эксепшена в худшую сторону, потому что нам приходится принимать решение "что делать дальше" в каждом месте. Либо мы пишем
{ok, Val} = func()
и получаем тот-же самый эксепшен. При этом теряем (почти, можно воспользоваться begin...end но это громоздко) возможность делать fun(fun(fun))).


понедельник, 21 января 2013 г., 16:15:31 UTC+2 пользователь JLarky написал:

Max Lapshin

unread,
Jan 21, 2013, 9:38:37 AM1/21/13
to erlang-...@googlegroups.com
Очень просто.

badmatch,   {error, {cannot_open_log_file,"log/system.log"}} in mymodule.erl:254

а с throw стектрейс будет слегка другой. Это раз.
Два: меня не всегда интересует отработает функция или нет.

Три: иногда удобно сделать так:

[Value || {ok, Value} <- [handle(Element) || Element <- Elements]]




2013/1/21 ufm <ufm...@gmail.com>
--

ufm

unread,
Jan 21, 2013, 9:49:29 AM1/21/13
to erlang-...@googlegroups.com


понедельник, 21 января 2013 г., 16:38:37 UTC+2 пользователь Max Lapshin написал:
Очень просто.

badmatch,   {error, {cannot_open_log_file,"log/system.log"}} in mymodule.erl:254

а с throw стектрейс будет слегка другой. Это раз.
Как меня поправили - error() вместо throw().
{'EXIT',{some_error, _}}
 
Два: меня не всегда интересует отработает функция или нет.
catch fun().

Три: иногда удобно сделать так:

[Value || {ok, Value} <- [handle(Element) || Element <- Elements]]
[Value || Value <- [handle(Element) || Element <- Elements]]
Если я правильно понимаю, работать будет во всех случаях один в один

Макс, я не пытаюсь сказать что ты не прав, упаси бог. Вобще тут больше вкусовщина. Я просто пытаюсь понять - может есть еще более вкусные конфеты, чем те, к которым я привык?

JLarky

unread,
Jan 21, 2013, 9:54:00 AM1/21/13
to erlang-...@googlegroups.com
>> а с throw стектрейс будет слегка другой. Это раз.
> Как меня поправили - error() вместо throw().
> {'EXIT',{some_error, _}}
throw это flow control, а error это ошибка. Как я уже говорил выше
throw в эрланге нужен только для эмуляции return императивных языков

>> Два: меня не всегда интересует отработает функция или нет.
> catch fun().

вызов функции через try catch дороже по процессору, тем более {error,
Error} это одно, а {'EXIT', ...} это другое

>> Три: иногда удобно сделать так:
>> [Value || {ok, Value} <- [handle(Element) || Element <- Elements]]
> [Value || Value <- [handle(Element) || Element <- Elements]]
> Если я правильно понимаю, работать будет во всех случаях один в один

конечно нет, Максов вариант откидывает любое сообщение не проходящее
по паттерну {ok, Value}, то есть любой другой терм в его список не
попадает, а у вас попадает.

Max Lapshin

unread,
Jan 21, 2013, 10:03:09 AM1/21/13
to erlang-...@googlegroups.com
Мой вариант с {ok, Value} не просто вернет все Value, он для начала отработает в то время, как вариант с эксепшнами свалится на первом же.

Я пробовал писать в стиле return value и throw/error, но пришел к коду, забитому catch с теми же case.

Дело в том, что ошибку иногда надо трансформировать из {error, enoent} в {error, {no_database_file, Path}}. В этом случае {error, Code} удобнее.


2013/1/21 JLarky <jla...@gmail.com>

ufm

unread,
Jan 21, 2013, 10:06:14 AM1/21/13
to erlang-...@googlegroups.com


понедельник, 21 января 2013 г., 16:54:00 UTC+2 пользователь JLarky написал:
>> а с throw стектрейс будет слегка другой. Это раз.
> Как меня поправили - error() вместо throw().
> {'EXIT',{some_error, _}}
throw это flow control, а error это ошибка. Как я уже говорил выше
throw в эрланге нужен только для эмуляции return императивных языков
Ну на самом деле return это return, а throw/error это другое. По крайней мере я не пытаюсь эмулировать одно через другое.

>> Два: меня не всегда интересует отработает функция или нет.
> catch fun().
вызов функции через try catch дороже по процессору, тем более {error,
Error} это одно, а {'EXIT', ...} это другое

>> Три: иногда удобно сделать так:
>> [Value || {ok, Value} <- [handle(Element) || Element <- Elements]]
> [Value || Value <- [handle(Element) || Element <- Elements]]
> Если я правильно понимаю, работать будет во всех случаях один в один
конечно нет, Максов вариант откидывает любое сообщение не проходящее
по паттерну {ok, Value}, то есть любой другой терм в его список не
попадает, а у вас попадает.

Да, моя ошибка - я достаточно редко пользуюсь этой конструкцией и просто забыл, что "{ok,X} <-"  работает не точно так-же как "{ok, X} ="

Собственнно на вкус и цвет все фломастеры разные, и было бы глупо использовать одну парадигму поведения во всех случаях, иногда даже в ущерб удобству.
Но я рассматривал в большей степени ситуацию, когда функции  деваться некуда. Например
io:format("~k",[1]).
** exception error: bad argument
     
in function  io:format/3
        called
as io:format(<0.25.0>,"~k",[1])


Понятное и ожидаемое поведение.

Dmitrii Dimandt

unread,
Jan 21, 2013, 10:13:55 AM1/21/13
to erlang-...@googlegroups.com

> Hi!
>
> Дискуссионный вопрос.
>
> Мне кажется, что парадигму "дай ей умереть" некоторые программисты восприняли как "ура, ничего не проверяем". В результате имеем то, что я описал в топике про монгодб -
> emongo:hex2dec("Test").
> <<"Nt">>
>



Безусловно. «fail early, fail often», имхо, работает только в контексте, когда этот самый фейл обвешан супервизорами, мониторами и т.п. — обвязкой, которая способна перехватить этот фейл и предпринять по этому поводу осознанные шаги.

Max Lapshin

unread,
Jan 21, 2013, 10:15:42 AM1/21/13
to erlang-...@googlegroups.com
Если честно, то в io:format как раз неудачно сделано журналирование ошибки. Найти, какая именно переменная из 20 переданных вызвала bad format, очень непросто.

Могу вот что сформулировать:

throw/error хороши для ограничения входных данных. Я сам люблю прописывать ограничения для входных данных:

read_db_entry(#db{count = Count} = DB, Entry) when Entry > 0 andalso Entry =< Count ->
  db_is_opened(DB) orelse error(db_not_opened),
  ....

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

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


P.S.  throw — это _именно_ return и он _именно_ для этого служит. Особенно он удобен в gen_server, потому что наверху handle_call стоит catch, который прячет разницу между обычным return и throw, т.е. можно так:

handle_call(Request, _From, State) ->
  is_valid_request(Request) orelse throw({reply, {error, invalid_request}, State}),
  ...
  {reply, {ok, Value}, State};



Последний пример, кстати, иллюстрирует откуда растут {error, Code}: из gen_server непросто выбросить исключение, а вернуть кортеж можно.




2013/1/21 ufm <ufm...@gmail.com>

--

JLarky

unread,
Jan 21, 2013, 10:26:37 AM1/21/13
to erlang-...@googlegroups.com
> Я пробовал писать в стиле return value и throw/error, но пришел к коду,
> забитому catch с теми же case.
мы пришли к коду типа (пишу псевдокод)
handle("url1/"++_, Params) ->
Name = get_value(name, Params),
Age = get_value(age, Params),
is_integer(Age) orelse throw({error, 400, <<"invalid age">>}),
is_valid_name(Name) orelse throw({error, 400, <<"invalid name">>}),
....
% используем Name и Age уверенно зная, что они валидные
{json, Response}.

ну и сервер делает типа
try Module:handle(Url, Params) of
{json, Data} ->
render_json(200, Data)
catch
{error, Status, Msg} ->
render_json(Status, Msg)
end.

как видишь ниодного case :) и да, как видишь throw используется только
как flow control, все эксепшены будут проброшены выше.

ufm

unread,
Jan 21, 2013, 10:28:17 AM1/21/13
to erlang-...@googlegroups.com

понедельник, 21 января 2013 г., 17:03:09 UTC+2 пользователь Max Lapshin написал:
Мой вариант с {ok, Value} не просто вернет все Value, он для начала отработает в то время, как вариант с эксепшнами свалится на первом же.
[V||V <- [catch 10/X || X <- [1,2,3,4,0,5]], is_float(V)].  
[10.0,5.0,3.3333333333333335,2.5,2.0]


Хотя, конечно, не сказать что очень красиво :)
 

ufm

unread,
Jan 21, 2013, 10:30:35 AM1/21/13
to erlang-...@googlegroups.com


понедельник, 21 января 2013 г., 17:15:42 UTC+2 пользователь Max Lapshin написал:
Если честно, то в io:format как раз неудачно сделано журналирование ошибки. Найти, какая именно переменная из 20 переданных вызвала bad format, очень непросто.

Могу вот что сформулировать:

throw/error хороши для ограничения входных данных. Я сам люблю прописывать ограничения для входных данных:

read_db_entry(#db{count = Count} = DB, Entry) when Entry > 0 andalso Entry =< Count ->
  db_is_opened(DB) orelse error(db_not_opened),
  ....

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

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

Собственно, я примерно к такому же выводу и пришел. Так что, похоже, нет вкуснее конфеты :)


P.S.  throw — это _именно_ return и он _именно_ для этого служит. Особенно он удобен в gen_server, потому что наверху handle_call стоит catch, который прячет разницу между обычным return и throw, т.е. можно так:

handle_call(Request, _From, State) ->
  is_valid_request(Request) orelse throw({reply, {error, invalid_request}, State}),
  ...
  {reply, {ok, Value}, State};
Оппа, а про такой способ - не знал. Спасибо.
 

JLarky

unread,
Jan 21, 2013, 10:30:23 AM1/21/13
to erlang-...@googlegroups.com
2013/1/21 ufm <ufm...@gmail.com>:
и не поленюсь сказать что медленее, потому что catch.

ufm

unread,
Jan 21, 2013, 10:43:10 AM1/21/13
to erlang-...@googlegroups.com
F1 = fun() -> [V||V <- [catch 10/X || X <- lists:seq(1,1000000)], is_float(V)] end.
F2
= fun() -> [V||V <- [10/X || X <- lists:seq(1,1000000)], is_float(V)] end.
timer
:tc(F1).  
{4760955,
[..]
timer
:tc(F2).                                                                      
{4535511,

Ну не сказать, что прям "ужас-ужас".

понедельник, 21 января 2013 г., 17:30:23 UTC+2 пользователь JLarky написал:

JLarky

unread,
Jan 21, 2013, 10:50:00 AM1/21/13
to erlang-...@googlegroups.com
>> В случае, если проблема создалась где-то в недрах нашей функции, то тут
>> уже надо рассматривать как и почему удобнее код ошибки или error.
>>
> Собственно, я примерно к такому же выводу и пришел. Так что, похоже, нет
> вкуснее конфеты :)

мне кажется лучше всего на эту тему холивары в golang, потому что там
эксепшенов вообще нет, только return и ошибки n-ым аргументом
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/kqGL_2p_VCc%5B1-25-false%5D

Max Lapshin

unread,
Jan 21, 2013, 10:52:42 AM1/21/13
to erlang-...@googlegroups.com
Ну почему сразу холивар, откуда такое желание сразу стянуть тему в срач?

Вопрос то действительно серьезный: отсутствие понимания, как правильнее приводит к бардаку в коде, когда забываешь, когда именно решил.

В итоге функция уже поменялась на {error, Code}, а ты всё ещё пишешь:  Value = my_function() и вместо эксепшна здесь получаешь его сильно позже и непонятно почему.


2013/1/21 JLarky <jla...@gmail.com>

JLarky

unread,
Jan 21, 2013, 11:04:44 AM1/21/13
to erlang-...@googlegroups.com
> F1 = fun() -> [V||V <- [catch 10/X || X <- lists:seq(1,1000000)],
> is_float(V)] end.
> F2 = fun() -> [V||V <- [10/X || X <- lists:seq(1,1000000)], is_float(V)]
> end.
> timer:tc(F1).
> {4760955,
> [..]
> timer:tc(F2).
> {4535511,
>
> Ну не сказать, что прям "ужас-ужас".

ну так это смотря что тестить, то есть в данном случае тестироваться
будет явно не catch.
1> element(1, timer:tc(fun() -> [V||V <- [10/X || X <-
lists:seq(1,100000)], is_float(V)] end)).
462879
2> element(1, timer:tc(fun() -> [10/X || X <- lists:seq(1,100000)]
end)).
157205
3> element(1, timer:tc(fun() -> [X+0 || X <- lists:seq(1,100000)] end)).
158523
4> element(1, timer:tc(fun() -> [X || X <- lists:seq(1,100000)] end)).
76349
5> element(1, timer:tc(fun() -> lists:seq(1,100000) end)).
2197
6> element(1, timer:tc(fun() -> [catch X+0 || X <- lists:seq(1,100000)] end)).
171605
7> element(1, timer:tc(fun() -> [catch X || X <- lists:seq(1,100000)] end)).
105128
8> element(1, timer:tc(fun() -> [catch catch catch catch X || X <-
lists:seq(1,100000)] end)).
129113
то есть потяря на уровне 10-30%
И это кстати при условии, что мы не создаём громоздкие туплы с {'EXIT', _}
19> element(1, timer:tc(fun() -> [{X/1} || X <- lists:seq(1,100000)]
end)).
216348
20> element(1, timer:tc(fun() -> [catch {X/1} || X <-
lists:seq(1,100000)] end)).
265867
21> element(1, timer:tc(fun() -> [catch {X/0} || X <-
lists:seq(1,100000)] end)).
698053
так что тут думаю видно почему {error, Error} было бы лучше

Alexey Romanov

unread,
Jan 21, 2013, 11:04:58 AM1/21/13
to erlang-...@googlegroups.com
> В итоге функция уже поменялась на {error, Code}, а ты всё ещё пишешь:  Value = my_function() и вместо эксепшна здесь получаешь его сильно позже и непонятно почему.

Значит, нужно покрывать тайп-спеками.

Yours, Alexey Romanov


2013/1/21 Max Lapshin <max.l...@gmail.com>

ufm

unread,
Jan 21, 2013, 11:26:07 AM1/21/13
to erlang-...@googlegroups.com
Ну собственно я рассматрмваю catch как исключительную ситуацию. А если каждый вызов приходится в catch оборачивать - тут понятно что проще error вернуть, так как это штатное поведение фунции.

Daniil Churikov

unread,
Jan 21, 2013, 12:23:08 PM1/21/13
to erlang-...@googlegroups.com
Уважаемый ufm, с вами не согласен, поясню почему.
Когда функция возвращает {ok, Val} | {error, Reason} в коде я могу написать
{ok, Val} = some_fun()
тем самым как бы говоря -- меня интересует только положительный результат. Могу так же завернуть это в case. А могу и проигнорировать возвращаемый результат вообще. Но когда вы внутри функции брасаете эксепшен, вы не оставляете мне выбора, кроме как заворачивать все вызовы в try/catch.
Вторая причина, документировать функции в формате -spec понятно как, и диалайзером потом проверять. А как задокументировать что у вас вылетает исключение?
И тестированию код без ексепшенов поддается лучше.

понедельник, 21 января 2013 г., 18:35:13 UTC+4 пользователь ufm написал:

ufm

unread,
Jan 21, 2013, 8:21:57 PM1/21/13
to erlang-...@googlegroups.com
Давайте на примере.

Есть база данных.
Есть две функции: readDB и writeDB - они читают и пишут в базу.

У нас задача - склад. И нам нужна функция, которая должна уметь отнять от количества находящегося на складе и вернуть сколько там осталось. При этом отрицательное количество остаться не может. Грубо говоря она выглядит так:
dec(S, Count) is_list(S), is_integer(Count), Count > 0 ->
 I
= readDB(S, count),
 R
= I - Count,
 
if
  R
< 0 -> {error, not_enough};
 
true -> writeDB(S, count, R), {ok, R}
 
end
.


Здесь использование {ok,...} оправдано. Потому что такое поведение у функции навеяно бизнеслогикой. Но при этом:
могут быть проблемы с базой данных. и мы их не ловим, так как на этом уровне мы ничего не можем предпринять.
Ключа S может не быть в базе. И это мы тоже не ловим, потому-что тут явная проблема с данными, и опять таки мы в этом месте ничего не можем с этим сделать.
count может оказаться в базе не integer а что-то другое (база mongodb и кто-то руками вбил в документ count :  "дофига").
И использовать эту фунцию мы будем как case dec("столы",10) of {ok,_} ->ok; {error,_} -> error end. И только если в том месте где мы вызываем dec мы можем принять решение "что делать если всё пошло не так", то мы оборачиваем её в try/catch и ловим всякие непонятки.

Т.е. мой подход - разделять "плохо/хорошо" в понятиях бизнеслогики и "плохо/хорошо" на уровне "окружающей среды".


понедельник, 21 января 2013 г., 19:23:08 UTC+2 пользователь Daniil Churikov написал:

Max Lapshin

unread,
Jan 21, 2013, 10:59:06 PM1/21/13
to erlang-...@googlegroups.com



2013/1/22 Sergiy Kostyushkin <s.kost...@gmail.com>
Любой ексепшен это сайд эффект для функции.
Какой смысл делать выбор в сторону функционального программирования и потом налево и направо использовать исключения?

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

 
Насчет применимости в бизнес-логике тоже весьма спорно, эту бизнес-логику вероятно потом кому-то придется читать и возможно менять. Чистая функция всегда читабельнее.

Ну это же неверно.

save_payment(#user{} = User, #payment{} = Payment) ->
  payment_belongs_to_user(User,Payment) orelse error(payment_doesnt_belong_to_user),
  user_has_enough_money(User,Payment) orelse error(not_enough_money),
  ...
  ...
  save_payment_to_db(Payment),
  ok.

Вместо такого читабельного кода вы предлагаете что? Правильно, адскую портянку из case или сорок функций, запомнить порядок вызова которых невозможно. Спасибо, не надо.

Serge Matveenko

unread,
Jan 22, 2013, 1:49:48 AM1/22/13
to erlang-...@googlegroups.com
2013/1/22 Max Lapshin <max.l...@gmail.com>:

> Вместо такого читабельного кода вы предлагаете что? Правильно, адскую
> портянку из case или сорок функций, запомнить порядок вызова которых
> невозможно. Спасибо, не надо.

if-ов-то нет, да. Практичный язык, ага?

--
Serge Matveenko
mailto: se...@matveenko.ru
github: http://lnkfy.com/1
linkedin: http://lnkfy.com/S

Max Lapshin

unread,
Jan 22, 2013, 5:54:35 AM1/22/13
to erlang-...@googlegroups.com
Сергей, простите, но я вообще не понимаю, что вы говорите и зачем.


2013/1/22 Serge Matveenko <se...@matveenko.ru>

Dmitrii Dimandt

unread,
Jan 22, 2013, 6:04:40 AM1/22/13
to erlang-...@googlegroups.com

Любой ексепшен это сайд эффект для функции.
Какой смысл делать выбор в сторону функционального программирования и потом налево и направо использовать исключения?

Насчет применимости в бизнес-логике тоже весьма спорно, эту бизнес-логику вероятно потом кому-то придется читать и возможно менять. Чистая функция всегда читабельнее.


Поменьше идеализма и будет счастье. Эрланг является практическим и прагматическим языком в первую и функциональным языком во вторую очередь. В дереве супервизоров проще и понятнее может быть именно вылететь нафиг и отловить и обработать эту ошибку в вышестоящем супервизоре, чем городить огород из case'ов, обрабатывающих каждый возможный {error, …} что приходит из вызываемых функций.

Ну и ниже, в отцитированном, уже все описано

Понеділок, 21 січня 2013 р. 16:35:13 UTC+2 користувач ufm написав:

Sergiy Kostyushkin

unread,
Jan 22, 2013, 11:30:38 AM1/22/13
to erlang-...@googlegroups.com
Вівторок, 22 січня 2013 р. 05:59:06 UTC+2 користувач Max Lapshin написав:


2013/1/22 Sergiy Kostyushkin <s.kost...@gmail.com>
Любой ексепшен это сайд эффект для функции.
Какой смысл делать выбор в сторону функционального программирования и потом налево и направо использовать исключения?

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


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

 
Насчет применимости в бизнес-логике тоже весьма спорно, эту бизнес-логику вероятно потом кому-то придется читать и возможно менять. Чистая функция всегда читабельнее.

Ну это же неверно.

save_payment(#user{} = User, #payment{} = Payment) ->
  payment_belongs_to_user(User,Payment) orelse error(payment_doesnt_belong_to_user),
  user_has_enough_money(User,Payment) orelse error(not_enough_money),
  ...
  ...
  save_payment_to_db(Payment),
  ok.

Вместо такого читабельного кода вы предлагаете что? Правильно, адскую портянку из case или сорок функций, запомнить порядок вызова которых невозможно. Спасибо, не надо.

Нет не правильно! Такой код я считаю оправданым. Но это если рассматривать его вырваным из контекста, потому что в отдельно взятой задаче такой код может оказатся неоправданым, и его можно было избежать если решать задачу более комплексно.

Reply all
Reply to author
Forward
0 new messages