суббота, 10 декабря 2011 г.

Посетил sef.by to students

Привет! Сегодня побывал на, дословно, «Форум по программной инженерии для студентов».

В целом, мне понравилось :). Разные были доклады, относительно много, некоторые немного скучные, некоторые весёлые. Нового ничего такого не узнал, тайну мира не открыли (чего, в принципе и не должно было быть на данном форуме). Однако я увидел, что вот, есть люди, которые что-то делают и у них есть в этом успех, что вот, есть крутые компании в Беларуси, а не мифы всё это, что есть стартаперы, есть какие-то тусовки и прочее-прочее-прочее.

Жаль, конечно, что практически вся сфера IT, как там и сказали, находится в Минске. Если бы в моём городе были бы такие мероприятия, то было бы замечательно.

Из докладчиков мне понравились 3 человека:

Микита Микадо — стартапер, CEO QuoteRoller и CodingStaff. Эдакий тусовщик, сбежавший из универа в Америку, ну и окунулся в это всё. Рассказывал, если в двух словах, про то, что начинать нужно СЕЙЧАС. Неважно что, главное начинать и делать. Хоть драйвер для принтера ;).

Влад "mend0za" Шахов — рассказывал про Linux, использование линукса во встроенных системах и всё такое связанное. В двух словах — используй linux везде, где ты хочешь написать свою ось. Т.е. вместо того чтобы на навороченный чайник под микроконтроллер писать свой кривой велосипед лучше взять ядро линукс, написать драйверов подогрева воды и всё будет лучше, совместимее и так далее.

Павел Габриэль — говорил "о жизни". Про то, что такое работа, как мы её себе представляем. Про то, что наше представление о мире формирует реальность и прочие вещи. Слайды были понятные, без кучи текста. Он единственный на этом форуме не был «рабом» презентации. Слайды были для него, а не он для слайдов, как у большинсва остальных. Т.е. он как-бы подкреплял свои речи слайдами, а не объяснял, что на слайдах там такое. В общем, Стив Джобс-лайк. Алсо, у него был макбук с наклейкой mongoDB.

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

Немного по поводу игры. Тут внезапно оказалась, что следующая неделя — зачётная. Я вообще ничего не знал об этом. Так что мы решили заморозить любую разработку до того, пока не сдадим все экзамены и не закончится сессия. Я надеюсь, что таки сдам зачёты с экзаменами и не вылечу с первого же курса (хотя по правде, я этого больше всего хочу, но родители будут не рады).

Пока ехали обратно в поезде, придумали с другом идею для Flash/iPhone/Android-игры забавной. Можно будет после сессии немного продлить заморозку социалки и попробовать реализовать по-быстрому эту идею, заработать немного денег. Может на ноут хватит. Хотя с моей ленью вряд ли я до конца сессии ещё в университете буду.

пятница, 18 ноября 2011 г.

Текущий проект

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

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

Ну а делать что-то нужно, без дела слишком уж уныло становится через какое-то время. Поэтому решили с друзьями делать тоже социалку. Тем более что у них тоже были идеи и они мне понравились :).

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

Игра про магию. Больше толком ничего не расскажу. Мы ориентируемся на социалку, однако по стилю игры это не совсем «тупо нажал, получил пряник, подарил пряник другу», там даже есть онлайновые PvP-битвы между игроками. Ну и их заменители, если с онлайном не ок.

Если будет чего рассказать по архитектуре, или ещё по чём-нибудь, то напишу. Тут я не ограничен никакими неразглашениями.

P.S. Эти картинки — скриншоты из экрана создания персонажа, если что :)

суббота, 12 ноября 2011 г.

Определение места badmatch при вызове одинаковых функций

Итак, допустим, что у вас есть вот такой код:

sometimes_not_ok(42) ->
  not_ok;
sometimes_not_ok(_) ->
  ok.
test() ->
  ok = sometimes_not_ok(13),
  ok = sometimes_not_ok(42).
Функция sometimes_not_ok/1 возможно говорит с бд, или ещё чего-нибудь такого и может выбрасывать исключения/возвращать значения, которые никак не характеризуют аргументы, переданные ей.

У нас есть 2 пути, как сделать так, чтобы мы знали, в каком месте случился фейл. Первый из них заключается в том, чтобы обернуть всё в case'ы:


case sometimes_not_ok(13) of
  ok ->
    case sometimes_not_ok(42) of
      ok ->
        continue_here;
      not_ok ->
        not_ok_processing
    end;
  not_ok ->
    not_ok_processing
end.
Однако согласитесь, код при этом станет выглядеть ужасно. А если вам нужно будет поменять две строчки местами? А если у вас сотня таких вызовов? Да и это какое-то дефендное программирование выходит, у эрланга несколько другая концепция.

Второй же наш вариант, использовать try/catch:

try
  ok = sometimes_not_ok(13),
  ok = sometimes_not_ok(42)
catch
  error:{badmatch, not_ok} ->
    not_ok_happened
end.
Уже намного лучше. Первоначальный код виден, его можно легко поправить, нет кучи вложенных условий. Однако... когда произойдёт {badmatch, not_ok}, мы не будем знать, на какой строке он произошёл, там где мы передали 18, или же 42? А вот нам позарез нужно выполнять там какие-то действия, если не ок.

Ну и мне пришло в голову, что можно просто обернуть вызов функции в tuple. Вот так:

try
  {_, ok} = {first, sometimes_not_ok(13)},
  {_, ok} = {second, sometimes_not_ok(42)}
catch
  error:{badmatch, {first, not_ok}} ->
    not_ok_happened_first;
  error:{badmatch, {second, not_ok}} ->
    not_ok_happened_second
end.
И тогда мы с лёгкостью можем определить, в каком из мест у нас badmatch. Конечно, внешний вид кода стал немножечко не таким красивым, но ведь согласитесь, так намного лучше, чем с кучей case'ов?

Может быть, конечно, мой «способ» всем давно известен, тогда уж ладно. Но мои знания всё равно растут :).





понедельник, 24 октября 2011 г.

Используем Erlang-макросы для сокращения повторений кода

Немного предисловия. Данную статью я хотел запостить на хабр, однако она не была отмодерирована и не попала даже в песочницу. Может я и правда написал что-то не нужное, не знаю, но поэтому пишу сюда.

В проекте, где я участвую, есть отдельный эрланг-модуль, у которого есть одна функция — request, следующего формата:
request(Cmd, Args, ClientInfo, MongoSettings) -> ...
Первым аргументом идёт binary(), который обозначает идентификатор метода (т.е. в этом модуле, назовём его api_methods, много-много определений функции request, с разными пре-заданными Cmd (паттерн матчинг, в общем)), второй — proplist с разными аргументами, ClientInfo — состояние клиента, MongoSettings — линк на монго-соединение, база, с которой мы работаем. В общем, это не так важно.

До сегодняшнего дня, определение метода выглядело примерно так:
request(<<"method1">>, Args, ClientInfo, MongoSettings) ->
  case {proplists:get_value(<<"arg1">>, Args, undefined),
        proplists:get_value(<<"arg2">>, Args, undefined)} of
    {Arg1, Arg2} when is_integer(Arg1) andalso
                      is_binary(Arg2) ->
      some_module:some_method(Arg1, Arg2, ClientInfo, MongoSettings);
    {_, _} ->
      {error_response, ClientInfo}
  end.
При этом иногда количество аргументов довольно велико. И всё это разрастается в одну большую кашу. И таких блоков под полсотни уже. Я решил, что хорошо бы это хоть как-то объединить в какой-нибудь паттерн.

Для этого я решил использовать макросы. Макрос, по сути, просто заменяет один кусок текста в вашей программе на другой (аналогично #define из C).

Итак, как минимум нам нужно следующее:

  • Идентификатор метода
  • Необходимые аргументы
  • Guard'ы для этих аргументов
  • Функция, в которую они будут переданы
Сделаем это:
-define(METHOD(BinaryString, Arguments, CallbackModule, CallbackFunction),
    request(BinaryString, Args, ClientInfo, MongoSettings) ->
      request_macros(Args, ClientInfo, MongoSettings, Arguments, {CallbackModule, CallbackFunction})
).
Тем самым мы определяем макрос METHOD, принимающий четыре аргумента: идентификатор метода, список его аргументов (об этом чуть далее), модуль и функция, которую нужно будет запустить.

Итак, формат аргументов будет следующим:
[ {<<"arg-name">>, [ guard1, guard2, ... , guardN]} ]
Формат Guard'а (извиняюсь, не знаю, как правильно перевести, не защитник же):
{module, function}
Где module — имя модуля, function — имя функции, которая принимает аргумент и возвращает либо true, либо false.

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

Его код:
request_macros(Args, ClientInfo, MongoSettings, Arguments, {CallbackModule, CallbackFunction}) ->
  try lists:foldr(fun({ArgName, Guards}, Filled) ->
      case proplists:get_value(ArgName, Args, undefined) of
        undefined -> % Если в proplist'е нет такого аргумента, то выходим из foldr'а
          throw(missmatch);
        Value ->
          % проходимся по списку гвардов, проверяем каждое значение,
          % если возвращает false — покидаем foldr, иначе заполняем список аргументами
          lists:foreach(fun({Module, Function}) ->
              CheckGuard = apply(Module, Function, [Value]),
              if
                not CheckGuard ->
                  throw(missmatch);
                true ->
                  ok
              end
          end, Guards),
          [Value | Filled]
      end
  end, [], Arguments) of
    % Если всё ок и ошибки нет, просто вызываем функцию, которую должны
    CommandArgs ->
      apply(CallbackModule, CallbackFunction, CommandArgs ++ [ClientInfo, MongoSettings]),
  catch
    % В случае, если что-то не сошлось, говорим, что запрос плохой
    throw:missmatch ->
      {error_response, ClientInfo}
  end.
Я думаю, что комментарии дают понять, что делает данная функция. Я поясню только две вещи.

Первое это то, что вместо foldl я использовал foldr, чтобы просто можно было написать [El | Arr], а не Arr ++ [El], по сути, я думаю, не особо важно, что тут использовать. Если нет, сообщите об этом, буду рад.

Ну и второе — довольно долго я не знал, как же выйти из foreach'а, fold[l | r], прочих таких обходов. И вот, пришла мысль, что можно же просто повалить этот обход исключением и словить его выше. Вот так вот.

Ну и как итог, вместо монструозных методов у меня вышли довольно небольшие (в среднем 1-5 строк) вызовы макросов. Т.е. мой пример превращается в:
?METHOD(<<"method1">>, [
            {<<"arg1">>, [{erlang, is_integer}]},
            {<<"arg2">>, [{erlang, is_binary}]}
    ], some_module, some_method);
Довольно локанично, не находите?

Если есть какие-то советы, или ярость, по поводу того, что нельзя так делать или я делаю что-то не так, призываю вас в комментарии.

Спасибо.