Re: Интерфейс vs. протокол.
От: jazzer Россия Skype: enerjazzer
Дата: 15.04.11 13:43
Оценка: 15 (2)
Здравствуйте, 0x7be, Вы писали:

0>В современных мэйнстримовых языках интерфейс представляет собой набор функций (сигнатур), которые комонент должен реализовывать. Однако контракт компонента часто подразумевает нечто большее чем просто номенклатура возможных вызовов (ну или сообщений, так тоже можно сказать). В частности, компоненты могут иметь протокол, то есть легальные последовательности вызовов.


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

Если сам объект — мы, имхо, входим на территорию монад и зависимых типов.

Если внешнее, то можно чего-то добиться уже сейчас, хоть это и криво и косо, но можно "из палок и веревок" (я буду юзать C++ как вполне себе мейнстримовый язык).
(плюсофобам дальше не смотреть, я знаю, что в других языках есть разные средства, заранее спасибо)
  Скрытый текст
Т.е. у тебя есть 3 состояния: Initial, Opened, Closed:
struct Initial {};
struct Opened  { std::string fname; }; // можно добавить параметров
struct Closed  {};

события Open, Read, Write, Close (с какими-то параметрами):
struct Open  { string fname; };
struct Read  { string& data_to_read; }; // по ссылке - для считанных данных
struct Write { string data_to_write; };
struct Close {};

и таблица разрешенных переходов, которая, собственно, и составляет наш интерфейс, где следующее состояние — то, что функция возвращает:
struct File
{
  fstream f;
  //          FROM     EVENT  TO      BODY (можно юзать from, ev, to)
  TRANSITION( Initial, Open , Opened, f.open(ev.fname.c_str()))
  TRANSITION( Opened , Read , Opened, f >> ev.data_to_read    )
  TRANSITION( Opened , Write, Opened, f << ev.data_to_write   )
  TRANSITION( Opened , Close, Closed, { f.close(); }          )
};

где TRANSITION — такой вот макрос (to вынесено явно, чтоб можно было его поменять в теле обработчика сообщения):
#define TRANSITION(FROM,EVENT,TO,...) \
  TO go(FROM from, const EVENT& ev) { TO to; __VA_ARGS__; return to; }

В принципе, это более-менее стандартная нотация для конечного автомата, лишь с той разницей, что состояние возвращается наружу, так как мы хотим иметь каждый раз новый тип, и сделать это внутри объекта не удастся.
Юзать надо примерно так (auto и инициализация фигурными скобочками — это из С++0x):
  std::string data_to_read( "new data" );
  File f;
  auto s0 = Initial();
  auto s1 = f.go( s0, Open{"asd0.zip"}   );
  auto s2 = f.go( s1, Write{"data0"}     );
  auto s3 = f.go( s2, Read{data_to_read} );
  auto s4 = f.go( s3, Close()            );

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

Если же нужна просто цепочка вызовов, и мы не хотим писать так:
f.go( f.go( f.go( f.go(
  Initial(), Open{"asd1.zip"} ), Write{"data1"} ), Read{data_to_read} ), Close()
);

то можно написать функцию, которая принимает кортеж событий, и статически проверяет его на валидность (decltype тоже из С++0x, возвращает тип выражения):
#define RET(...) -> decltype( __VA_ARGS__ ) { return __VA_ARGS__; }

// функтор для File::go (в С++0х нет шаблонных лямбд)
struct Go
{
  File& f;
  template< class InitState, class Event >
  auto operator()( InitState s, const Event& ev ) -> RET( f.go(s,ev) )
};

// функция для применения кортежа событий
template< class InitState, class EvSeq >
auto go_chain( File& f, InitState init_state, const EvSeq& t )
  -> RET( fold( t, init_state, Go{f} ))

Юзаем:
auto final_state = go_chain( f, Initial(), make_tuple(
  Open{"asd2.zip"}, Write{"data2"}, Read{data_to_read}, Close()
));

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

Кому интересно и кто хочет поиграться, могу выложить полный код — все компилируется и работает, gcc 4.4.4 -std=gnu++0x.

ЗЫ Конечно же, это игрушечный пример, тут даже ошибки не обрабатываются Обработка ошибок потребовала бы создания чего-то вроде монады Maybe.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 14:18
Оценка:
Здравствуйте, WolfHound, Вы писали:

VD>>неудобно. Тогда нужно вводить правило по котому для уникальных объектов операция доступа к члену (x.y) схожа с передачей "х" по ссылке (y(ref x)). Тогда код будет выглядить привычно, а цель достигаться.

WH>А ты читать то что я пишу не пробовал? Ну так для разнообразия?

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

VD>>Правда автоматическое закрытие файла можно сделать и проще. Если программист знает, что объект должен быть закрыт до выхода из области видимости, то достаточно конструкции типа use из F# или того же using-а. Так что профит не велик, если говорить только о закрытии файла.

WH>Профит номер раз: using забыть можно. А тут не забудешь.

Только это ни разу не проблема. Я даже не помню когда у меня такое случалось.

WH>Профит номер два: Можно безопасно передавать и возвращать из функции.


Скорее можно безопасно "оставлять" в функции ссылку тем самым разнося логику между несколькими объектами. Потому как если логика локализована в рамках одной функции, то достаточно обеспечить закрытие ресурса при покидание области видимости (что реализуется несравнимо проще).

В общем, понятно что выгода есть. Но не очевидно, что овчинка стоит выделки.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 14:21
Оценка:
Здравствуйте, adontz, Вы писали:

A>Ты абсолютно не прав. Я легко и просто могу оценить потенциальный выигрыш, просто зная количество stateful объектов.


Это другая идеолоия. Здесь оценки не уместны. Они не учитывают тучу нюансов. Я сто раз встречал болоболов-теоретиков признававших ненужными, неинтересными или даже вредными разные возможности которые лично мне были очевидны. И не раз видел как адекватные люди меняли мнение когда начинали использовать эти возможности на практике.

Так что правильно оценить фичу человек может только если она ему уже нужна, или если он использовал ее на практике.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 14:31
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Профит номер два: Можно безопасно передавать и возвращать из функции.


А в полях то такие ссылки можно хранить? Их ведь все время обновлять придется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 14:36
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Фактически ты предлагаешь сделать объекты явными конечными автоматами, где состояние автомата будет информацией времени компиляции (т.е. каждый шаг программы будет отражен в типе чего-нибудь — либо самого объекта, либо чего-то связанного).


Ты путаешь наличие и контроля за состоянием с конечными автоматами. КА — это только один из видов представления и управления состоянием. Обратное не верно. Отличительно особенностью КА является наличие явно описываемых состояний. Здесь этого нет. Это скорее инкапсуляция состояния.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Интерфейс vs. протокол.
От: jazzer Россия Skype: enerjazzer
Дата: 15.04.11 14:44
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, jazzer, Вы писали:


J>>Фактически ты предлагаешь сделать объекты явными конечными автоматами, где состояние автомата будет информацией времени компиляции (т.е. каждый шаг программы будет отражен в типе чего-нибудь — либо самого объекта, либо чего-то связанного).


VD>Ты путаешь наличие и контроля за состоянием с конечными автоматами. КА — это только один из видов представления и управления состоянием. Обратное не верно. Отличительно особенностью КА является наличие явно описываемых состояний. Здесь этого нет. Это скорее инкапсуляция состояния.


Если честно, не понял, что ты хотел сказать и что из твоих слов должно следовать.
Явно описываемые состояния у 0x7be есть — "можно позвать метод1", "нельзя позвать метод2", и т.д. Что ты понимаешь под инкапсуляцией здесь, я тоже не понял.
В общем, можешь по предложениям развернуть свою мысль?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[11]: Интерфейс vs. протокол.
От: adontz Грузия http://adontz.wordpress.com/
Дата: 15.04.11 14:56
Оценка:
Здравствуйте, VladD2, Вы писали:

A>>Ты абсолютно не прав. Я легко и просто могу оценить потенциальный выигрыш, просто зная количество stateful объектов.

VD>Это другая идеолоия. Здесь оценки не уместны. Они не учитывают тучу нюансов. Я сто раз встречал болоболов-теоретиков признававших ненужными, неинтересными или даже вредными разные возможности которые лично мне были очевидны. И не раз видел как адекватные люди меняли мнение когда начинали использовать эти возможности на практике.
VD>Так что правильно оценить фичу человек может только если она ему уже нужна, или если он использовал ее на практике.

Влад, как раз ты сейчас и балаболишь. вот у меня есть 1000 классов. И них 47 штук — stateful, а 953 stateless (как правило они ещё и immutable). То есть даже если этот метод распрекрасный и делает всё что только можно придумать, 47 классов это потолок его внедряемости.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 15:29
Оценка:
Здравствуйте, 0x7be, Вы писали:

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


Стремление похвальное. Только надо помнить, что можно перегнуть палку. Надо проверить создает ли на практике проблемы текущий подход (без этого контроля)? Даст ли ощутимые преимущества новый? И не создаст ли новый подход другие трудности, которые нивелируют преимущества?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[12]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 15:33
Оценка:
Здравствуйте, adontz, Вы писали:

A>Влад, как раз ты сейчас и балаболишь. вот у меня есть 1000 классов. И них 47 штук — stateful, а 953 stateless (как правило они ещё и immutable). То есть даже если этот метод распрекрасный и делает всё что только можно придумать, 47 классов это потолок его внедряемости.


То что у тебя есть сейчас — это продукт видения реализации в рамках знакомых тебе концепций. Это ровным счетом ничего не говорит. Уверен, что есть люди в чьих программах нет ни единого класса или даже хоть как-то сэмулированного АТД. Им попросту не знакомы эти концепции. И они свято верят, что все эти АТД и классы не нужны, так как "они же живут без них...".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Интерфейс vs. протокол.
От: adontz Грузия http://adontz.wordpress.com/
Дата: 15.04.11 15:35
Оценка:
Здравствуйте, VladD2, Вы писали:

A>>Влад, как раз ты сейчас и балаболишь. вот у меня есть 1000 классов. И них 47 штук — stateful, а 953 stateless (как правило они ещё и immutable). То есть даже если этот метод распрекрасный и делает всё что только можно придумать, 47 классов это потолок его внедряемости.

VD>То что у тебя есть сейчас — это продукт видения реализации в рамках знакомых тебе концепций. Это ровным счетом ничего не говорит. Уверен, что есть люди в чьих программах нет ни единого класса или даже хоть как-то сэмулированного АТД. Им попросту не знакомы эти концепции. И они свято верят, что все эти АТД и классы не нужны, так как "они же живут без них...".

Всё что не укладывается в мейнстримные языки мне не нужно тем более.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: Интерфейс vs. протокол.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.04.11 15:40
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Если честно, не понял, что ты хотел сказать и что из твоих слов должно следовать.


То и говорю. Наличие состояния и контроль за ним еще не означает, что это КА.

J>Явно описываемые состояния у 0x7be есть — "можно позвать метод1", "нельзя позвать метод2", и т.д. Что ты понимаешь под инкапсуляцией здесь, я тоже не понял.


КА — это реализация. А оных может быть много. С монадами знаком?

J>В общем, можешь по предложениям развернуть свою мысль?


Мысль проста. Не все что связывает вычисления является КА.

У тебя просто проявляется классическая болезнь — "когда в руках молоток, все кажется гвоздями".

В данном случае состояние и его изменение выражается через типы и объекты. На самом деле для тех же файлов все состояние будет заключаться в хранении дескриптора файла и позиции смещения внутри файла. Выражать это в виде КА никому даже в голову не придет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Интерфейс vs. протокол.
От: Pavel Dvorkin Россия  
Дата: 15.04.11 15:48
Оценка:
Здравствуйте, 0x7be, Вы писали:

0> В частности, компоненты могут иметь протокол, то есть легальные последовательности вызовов.


interface ISomething
{
 int Status{get; set;}
 void Open(...);

 void Read(...);
 void Write(...);
 
 void Close(...);


Каждый метод в последовательности проверяет и ставит этот Status. Open проверяет, равен ли он 2 и ставит в 0. Read-Write требует 0 и ставит 1. Close требует 1 и ставит 2. При некорректном значении — exception.

Порезан жуткий оверквотинг.
WolfHound
With best regards
Pavel Dvorkin
от модератора
От: WolfHound  
Дата: 15.04.11 15:57
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

Следи за тем сколько цитируешь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[9]: Интерфейс vs. протокол.
От: WolfHound  
Дата: 15.04.11 15:57
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>А в полях то такие ссылки можно хранить? Их ведь все время обновлять придется.

Почитай статью Re[5]: Интерфейс vs. протокол.
Автор: dimgel
Дата: 15.04.11

Там много чего интересного написано. В том числе и про хранение в полях и про замыкание уникальных ссылок.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: Интерфейс vs. протокол.
От: 0x7be СССР  
Дата: 15.04.11 16:01
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Стремление похвальное. Только надо помнить, что можно перегнуть палку. Надо проверить создает ли на практике проблемы текущий подход (без этого контроля)? Даст ли ощутимые преимущества новый? И не создаст ли новый подход другие трудности, которые нивелируют преимущества?

Дык, потому я тут и поднял тему, что мне самому это не очевидно, насколько это применимо в реальной жизни.
В моем проекте есть некий предвестник этого подхода:
interface IGuardedResource<T>
{
  IGuardedResourceSession<T> Aquire();
}

interface IGuardedResourceSession<T> : IDisposable
{
  T Resource { get; }
}

Эти два интерфейса как раз нечто подобное и реализовывали. И тут же я обдумывал вопрос, как не дать пользователю обратиться к ресурсу после Dispose`а в IGuardedResourceSession. Пришел к выводу, что средствами языка C# это сделать невозможно, поскольку T в данном варианте должен быть линейным типом.

В более масштабном варианте я свою идею не тестировал. Подозреваю, что Nemerle тут бы мог помочь с синтаксическим сахаром описания этих состояний. Если Nemerle поддерживает макросы, которые могут верифицировать код, то, может, и проблему с линейным типом решить можно.
Re[2]: Интерфейс vs. протокол.
От: 0x7be СССР  
Дата: 15.04.11 16:02
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Каждый метод в последовательности проверяет и ставит этот Status. Open проверяет, равен ли он 2 и ставит в 0. Read-Write требует 0 и ставит 1. Close требует 1 и ставит 2. При некорректном значении — exception.

Это убивает главный смысл моей затеи — статический контроль соблюдения протокола компилятором.
Re[3]: Интерфейс vs. протокол.
От: Pavel Dvorkin Россия  
Дата: 15.04.11 16:10
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Каждый метод в последовательности проверяет и ставит этот Status. Open проверяет, равен ли он 2 и ставит в 0. Read-Write требует 0 и ставит 1. Close требует 1 и ставит 2. При некорректном значении — exception.

0>Это убивает главный смысл моей затеи — статический контроль соблюдения протокола компилятором.

Хм. У меня некоторое сомнение насчет статического контроля. Представь себе, что вызывать Open надо из одного C# файла, Read-Write из другого, а Close из третьего. Почему так — не обсуждаем, может, это и не очень хороший стиль, но не запретишь же.

А можно и без трех файлов. Просто сложная логика открытий, чтений и закрытий, в которой только в рантайме и можно разобраться.

А еще наследование есть и переопределение виртуальных (может быть) Open, Read, Close.
With best regards
Pavel Dvorkin
Re[4]: Интерфейс vs. протокол.
От: 0x7be СССР  
Дата: 15.04.11 16:17
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Хм. У меня некоторое сомнение насчет статического контроля. Представь себе, что вызывать Open надо из одного C# файла, Read-Write из другого, а Close из третьего. Почему так — не обсуждаем, может, это и не очень хороший стиль, но не запретишь же.

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

PD>А еще наследование есть и переопределение виртуальных (может быть) Open, Read, Close.

Это вопрос, да.
Но тут можно разные подходы реализовывать.
Протокол может задавать базовый класс или же он может навешиваться фасадом.
Re[5]: Интерфейс vs. протокол.
От: Pavel Dvorkin Россия  
Дата: 15.04.11 16:19
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Хм. У меня некоторое сомнение насчет статического контроля. Представь себе, что вызывать Open надо из одного C# файла, Read-Write из другого, а Close из третьего. Почему так — не обсуждаем, может, это и не очень хороший стиль, но не запретишь же.

PD>>А можно и без трех файлов. Просто сложная логика открытий, чтений и закрытий, в которой только в рантайме и можно разобраться.
0>Не совсем понял, чем это может помешать и как такое может быть.
0>Можно как-то пример конкретный придумать?

Может, я неправильно понял, но я полагал, что под статическим контролем компилятора ты имеешь в виду контроль в дизайн-тайме. А файлы компилируются независимо.
With best regards
Pavel Dvorkin
Re[6]: Интерфейс vs. протокол.
От: WolfHound  
Дата: 15.04.11 16:27
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Может, я неправильно понял, но я полагал, что под статическим контролем компилятора ты имеешь в виду контроль в дизайн-тайме. А файлы компилируются независимо.

А тебя не удивляет статический контроль за типами функций описанных в разных файлах?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.