Информация об изменениях

Сообщение Re[10]: Опциональные типы от 27.02.2017 2:38

Изменено 27.02.2017 6:44 vdimas

Re[10]: Опциональные типы
Здравствуйте, meadow_meal, Вы писали:

_>ИМХО, ClanId — должен быть int, а не Optional<int>. Скажем сервисная функция GetClanInfo(ClanId clan_id) не должна принимать NotSet, а GetPlayerInfo может вернуть NotSet в поле ClanId, и я бы предпочел, чтобы в обоих случаях это было отражено в сигнатуре.


Ну и какие проблемы, делаем так:
struct ClanId {
    public int Value { get {
        if(HasValue)
            return value_;
        throw ...
    }}

    public bool HasValue { get { return Value != 0; }}
}

ClanInfo GetClanInfo(int clan_id) {}

class PlayerInfo {
    ClanId clanId;
    ...
}

...
PlayerInfo pi = ...
ClanInfo ci;

if(pi.clanId.HasValue)
    ci = GetClanInfo(pi.clanId.Value);


У вас на прямо сейчас примерно так же и выглядит. Потому что публичный интерфейс у struct ClanId и struct Optional<int> идентичный (я ориентировался на Nullabl<int>, бо вашего Optional в глаза не видел, но не суть).


_>Одна из возможных мотиваций для констант после where — экономия трафика — тоже не проходит, так как если не использовать variable encoding, то для NonSet получим 4 байта вместо 1 байта (а часто — бита)


Обычно int передают в 7-мибитной кодировке.
А когда речь о передаче бита, то это обычно речь о передаче больших структур или массива структур через автогенеренный код, который точно так же в теле сериализации ветвится через точно такое же HasValue.


_>а с variable encoding мы стараемся быть осторожнее, так как в случае частого использования и большого числа мелких пакетов нагрузка на cpu может дать больше вреда, чем минимальная экономия трафика — пользы (впрочем, если до этого дойдет, нужно конечно замерять, задумываться об этом имеет смысл лишь для очень частых событий).


Еще раз.
Да какая разница, если код сериализации ориентируется на HasValue?


_>Здесь мы тоже не можем по типу отличить обязательно наличествующее значение от опционального.


Не понял, почему не можете, если в DSL описано:
type ClanId = Optional<int> where NotSet = 0;

Кто не может отличить?
Код сериализации?
Или программист, которому студия подскажет интиллисенсом?

Ты же сам говорил, что в пакете обновлений тебе придет Optional.
Ну вот пришло Optional<ClanId>, где сам ClanId — тоже эдакий optional.

Программист в вашем стиле пишет:
void UpdateXXX(PlayerPDU pdu) {
   if(pdu.clanId.HasValue) {
      player.clanId = pdu.clanId.Value;
      player.clanInfo = GetClanInfo(player.clanId);
   }
   ...
}

class ClanInfo {...}

ClanInfo GetClanInfo(ClanId clanId) {
    if(!clanId.HasValue)
        return null;

    return RetrieveClanInfoFromCacheOrServer(clanId.Value);
}



_>Небольшая экономия памяти не кажется достаточной компенсацией.


Для простых int это экономия вдвое.
И для игр экономия памяти и эффективная её разметка — это первое дело.
Просто у вас объектов в играх, похоже, немного или активность объектов низкая, поэтому вы не паритесь.


_>Ну и не работает обобщенный код для Nullable и ??, что может быть или не быть неудобством в конкретных случаях.


Тю.
interface INullable<T> {
    bool HasValue { get; }
    T Value { get; }
}

struct ClanId : INullable<int> {...}

void SerializeHeader(T nullable, Formatter<bool> f) where T : INullable<T>
{
    f.Write(nullable.HasValue);
}

void SerializeBody(T nullable, Formatter<T> f) where T : INullable<T>
{
    if(nullable.HasValue)
        f.write(nullable.Value);
}

В заголовке пакета идёт биты для nullable-полей, далее сами поля.


_>Но честно говоря, я не понимаю, зачем нужно искать замену Optional в случае его использования по самому что ни на есть прямому назначению.


Потому что есть только негодный Nullable в системной библиотеке, с отсутствующей инфраструктурой и нулевой функциональностью.

Посмотри сюда:
https://github.com/nlkl/Optional/blob/master/Optional/Option_Maybe.cs
ValueOr/Or/Else/Filter/FlatMap — и т.д.

Утилитный код по ссылке можно организовать в виде методов-расширений для INullable<T>.

А вот это: Contains/Exists/GetEnumerator — весьма остроумно, ИМХО.
Позволяет рассматривать Option<> в том числе как пустой список (нет значения), либо как список с единственным элементом (есть значение).
Тоже для обхода/сериализации подойдёт.

Такие вещи можно и нужно пробовать. Глядишь, понравится всяко больше, чем встроенный Nullable.


_>Нужно ли что-то менять в системе апдейт-рекордов — вопрос для нас еще открытый.


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

Разумеется, твоя задача не бросаться сходу делать как подсказывают, а лишь коллекционировать идеи, среди которых потом обычно "выстреливает" не какая-то одна, а удачная комбинация их.
Re[10]: Опциональные типы
Здравствуйте, meadow_meal, Вы писали:

_>ИМХО, ClanId — должен быть int, а не Optional<int>. Скажем сервисная функция GetClanInfo(ClanId clan_id) не должна принимать NotSet, а GetPlayerInfo может вернуть NotSet в поле ClanId, и я бы предпочел, чтобы в обоих случаях это было отражено в сигнатуре.


Ну и какие проблемы, делаем так:
struct ClanId {
    public int Value { get {
        if(HasValue)
            return value_;
        throw ...
    }}

    public bool HasValue { get { return Value != 0; }}
}

ClanInfo GetClanInfo(int clan_id) {}

class PlayerInfo {
    ClanId clanId;
    ...
}

...
PlayerInfo pi = ...
ClanInfo ci;

if(pi.clanId.HasValue)
    ci = GetClanInfo(pi.clanId.Value);


У вас на прямо сейчас примерно так же и выглядит. Потому что публичный интерфейс у struct ClanId и struct Optional<int> идентичный (я ориентировался на Nullabl<int>, бо вашего Optional в глаза не видел, но не суть).


_>Одна из возможных мотиваций для констант после where — экономия трафика — тоже не проходит, так как если не использовать variable encoding, то для NonSet получим 4 байта вместо 1 байта (а часто — бита)


Обычно int передают в 7-мибитной кодировке.
А когда речь о передаче бита, то это обычно речь о передаче больших структур или массива структур через автогенеренный код, который точно так же в теле сериализации ветвится через точно такое же HasValue.


_>а с variable encoding мы стараемся быть осторожнее, так как в случае частого использования и большого числа мелких пакетов нагрузка на cpu может дать больше вреда, чем минимальная экономия трафика — пользы (впрочем, если до этого дойдет, нужно конечно замерять, задумываться об этом имеет смысл лишь для очень частых событий).


Еще раз.
Да какая разница, если код сериализации ориентируется на HasValue?


_>Здесь мы тоже не можем по типу отличить обязательно наличествующее значение от опционального.


Не понял, почему не можете, если в DSL описано:
type ClanId = Optional<int> where NotSet = 0;

Кто не может отличить?
Код сериализации?
Или программист, которому студия подскажет интиллисенсом?

Ты же сам говорил, что в пакете обновлений тебе придет Optional.
Ну вот пришло Optional<ClanId>, где сам ClanId — тоже эдакий optional.

Программист в вашем стиле пишет:
void UpdateXXX(PlayerPDU pdu) {
   if(pdu.clanId.HasValue) {
      player.clanId = pdu.clanId.Value;
      player.clanInfo = GetClanInfo(player.clanId);
   }
   ...
}

class ClanInfo {...}

ClanInfo GetClanInfo(ClanId clanId) {
    if(!clanId.HasValue)
        return null;

    return RetrieveClanInfoFromCacheOrServer(clanId.Value);
}



_>Небольшая экономия памяти не кажется достаточной компенсацией.


Для простых int это экономия вдвое.
И для игр экономия памяти и эффективная её разметка — это первое дело.
Просто у вас объектов в играх, похоже, немного или активность объектов низкая, поэтому вы не паритесь.


_>Ну и не работает обобщенный код для Nullable и ??, что может быть или не быть неудобством в конкретных случаях.


Тю.
interface INullable<T> {
    bool HasValue { get; }
    T Value { get; }
}

struct ClanId : INullable<int> {...}

void SerializeHeader(T nullable, Formatter<bool> f) where T : INullable<T>
{
    f.Write(nullable.HasValue);
}

void SerializeBody(T nullable, Formatter<T> f) where T : INullable<T>
{
    if(nullable.HasValue)
        f.write(nullable.Value);
}

В заголовке пакета идут биты для nullable-полей, далее сами поля.


_>Но честно говоря, я не понимаю, зачем нужно искать замену Optional в случае его использования по самому что ни на есть прямому назначению.


Потому что есть только негодный Nullable в системной библиотеке, с отсутствующей инфраструктурой и нулевой функциональностью.

Посмотри сюда:
https://github.com/nlkl/Optional/blob/master/Optional/Option_Maybe.cs
ValueOr/Or/Else/Filter/FlatMap — и т.д.

Утилитный код по ссылке можно организовать в виде методов-расширений для INullable<T>.

А вот это: Contains/Exists/GetEnumerator — весьма остроумно, ИМХО.
Позволяет рассматривать Option<> в том числе как пустой список (нет значения), либо как список с единственным элементом (есть значение).
Тоже для обхода/сериализации подойдёт.

Такие вещи можно и нужно пробовать. Глядишь, понравится всяко больше, чем встроенный Nullable.


_>Нужно ли что-то менять в системе апдейт-рекордов — вопрос для нас еще открытый.


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

Разумеется, твоя задача не бросаться сходу делать как подсказывают, а лишь коллекционировать идеи, среди которых потом обычно "выстреливает" не какая-то одна, а удачная комбинация их.