someFunc(L) when is_list(L) ->
haveSense(L) orelse throw(nosense_param),
someDo(L)
.
--
--
Страница рассылки: 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
2013/1/21 ufm <ufm...@gmail.com>:
Вообще для меня let it crash это про то как надо прежде чем что-то
писать понять как можно исправлять ошибки на более низком уровне. Типа
у нас может упасть VM, должен ли bash скриптик её запустивший
перезапустить vm или проверить скажем что на сервере не наступил oom
(ram или fs) из-за каких-то третих сервисов, может быть послать
сообщение админу?
если упал аппликейшен в vm нужно его перезапустить? отсигналить?
перезапустить весь vm?
если упал воркер, нужно его перезапустить? отсигналить, перезапустить
весь supervisor tree?
если упала функция хендлер, то мы должны порвать связь с клиентом его
породившим, или вернуть ему 500? или просто проигнорировать? или
записать в логи попытку хакерской атаки?
если упал вызов 3rd party библиотеки мы должны считать что кривые
данные, кривая библиотека, кривой сервис к которой она обращается?
и да, когда на все эти вопросы есть ответ, то можно let it crash,
потому что всегда сверхну найдётся кто-то, кто решит нашу проблему с
той или иной потерей функциональности будь то падение конкретного
запроса или переподключение клиента или переподключение всех клиентов.
--
а с throw стектрейс будет слегка другой. Это раз.Очень просто.badmatch, {error, {cannot_open_log_file,"log/system.log"}} in mymodule.erl:254
Два: меня не всегда интересует отработает функция или нет.
[Value || {ok, Value} <- [handle(Element) || Element <- Elements]]Три: иногда удобно сделать так:
>> Два: меня не всегда интересует отработает функция или нет.
> catch fun().
вызов функции через try catch дороже по процессору, тем более {error,
Error} это одно, а {'EXIT', ...} это другое
>> Три: иногда удобно сделать так:
>> [Value || {ok, Value} <- [handle(Element) || Element <- Elements]]
> [Value || Value <- [handle(Element) || Element <- Elements]]
> Если я правильно понимаю, работать будет во всех случаях один в один
конечно нет, Максов вариант откидывает любое сообщение не проходящее
по паттерну {ok, Value}, то есть любой другой терм в его список не
попадает, а у вас попадает.
>> а с 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}, то есть любой другой терм в его список не
попадает, а у вас попадает.
io:format("~k",[1]).
** exception error: bad argument
in function io:format/3
called as io:format(<0.25.0>,"~k",[1])
--
ну и сервер делает типа
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, все эксепшены будут проброшены выше.
Мой вариант с {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]
Если честно, то в 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};
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,
мне кажется лучше всего на эту тему холивары в golang, потому что там
эксепшенов вообще нет, только return и ошибки n-ым аргументом
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/kqGL_2p_VCc%5B1-25-false%5D
ну так это смотря что тестить, то есть в данном случае тестироваться
будет явно не 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} было бы лучше
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
.
Любой ексепшен это сайд эффект для функции.Какой смысл делать выбор в сторону функционального программирования и потом налево и направо использовать исключения?
Насчет применимости в бизнес-логике тоже весьма спорно, эту бизнес-логику вероятно потом кому-то придется читать и возможно менять. Чистая функция всегда читабельнее.
if-ов-то нет, да. Практичный язык, ага?
--
Serge Matveenko
mailto: se...@matveenko.ru
github: http://lnkfy.com/1
linkedin: http://lnkfy.com/S
Любой ексепшен это сайд эффект для функции.Какой смысл делать выбор в сторону функционального программирования и потом налево и направо использовать исключения?
Насчет применимости в бизнес-логике тоже весьма спорно, эту бизнес-логику вероятно потом кому-то придется читать и возможно менять. Чистая функция всегда читабельнее.
Понеділок, 21 січня 2013 р. 16:35:13 UTC+2 користувач ufm написав:
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 или сорок функций, запомнить порядок вызова которых невозможно. Спасибо, не надо.