Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, AndrewVK, Вы писали:
ВВ>>>Ну речь же про наследование. AVK>>Речь про отмену наследования.
ВВ>Ну очевидно надо сначала разобраться, что мы решили отменять.
ВВ>>>А с ФВП ты сам прекрасно ответил на вопрос, как же возможен динамик диспатч без наследования. AVK>>А такого вопроса никто не задавал. Вопрос был к AlexRK, как он предполагает реализовывать ДД.
ВВ>Речь идет о запрете наследования интерфейсов между собой. Реализация интерфейсов остается. Наследование классов, кстати, тоже можно убрать
Да, именно так. В какой-то момент произошла подмена понятий, что речь идет о запрете наследования вообще, причем в термин "наследование" мы также включаем и реализацию интерфейса классом. Это НЕВЕРНО.
Речь о запрете наследовать один интерфейс от другого и один класс от другого. Реализовывать интерфейс классом можно.
(Впрочем, для пущей крутизны можно интерфейс вообще не считать типом, как я выше писал.)
Здравствуйте, A13x, Вы писали:
A>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Здравствуйте, SV., Вы писали:
ВВ>>>>Я не знаю, что именно отражает ваш код, и какие именно мессенджеры существуют в жизни — а вы же по ходу уверены, что точно и наверняка знаете какие абстракции будут правильны в 100% случаев. Вот только в 100% эти абстракции оказываются неправильными. SV.>>>Это бывает, да. Но для того и меню Refactor, чтобы в момент осознания несогласованности вашей объектной модели с жизнью его нажимать. И если не лениться и рефакторить код каждый раз, когда это вылезает, то да, НА ПРАКТИКЕ в 100% случаев ваши абстракции будут, как вы выразились, "правильными" (я же имел в виду их неискуственность => удобочитаемость => поддерживаемость).
A>Можно обобщить — есть набор базовых действий которые могут совершать некоторые сущности, к примеру те же мессенджеры, пример: A>1. отправить текст A>2. отправить файл A>3. установить статус пользователя A>N. ... etc.
A>есть конкретный мессенджер, который может выполнять определенный набор действий, например скайп. Его тоже имеет смысл описать через интерфейс — исключительно для удобства потенциальных клиентов в то же время скрыв детали реализации. A>Иными словами SkypeMessenger должен быть интерфейсом, реализующим набор базовых интерфейсов-действий из 1..N (для облегчения тестирования моками, для снижения связности, для простоты наконец для конечных потребителей этого класса).
A>Используя IoC контейнер можно ссылаться на SkypeMessenger как на несколько раздельных интерфейсов внедренных как зависимости в некоторый класс, но я не вижу никаких преимуществ у такого решения.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Собственно, сабж. Что это дает-то в принципе? На первый взгляд кажется, что это вообще скорее вредно.
Вполне здравая идея отказаться от наследования если система типов это будет позволять.
Очевидно нужно будет ввести некие алиасы и объединения типов, чтобы не писать весь набор каждый раз.
Получается по сути почти то же, что и наследование но без сильной связи между интерфейсами.
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Собственно, сабж. Что это дает-то в принципе? На первый взгляд кажется, что это вообще скорее вредно.
_NN>Вполне здравая идея отказаться от наследования если система типов это будет позволять. _NN>Очевидно нужно будет ввести некие алиасы и объединения типов, чтобы не писать весь набор каждый раз. _NN>Получается по сути почти то же, что и наследование но без сильной связи между интерфейсами.
_NN>Старый вариант _NN>
Нужно только, чтобы можно было объявлять поля с типами таких алиасов, возвращать их из методов и т.п.
И вообще:
type ICollection = IEnumerable & ICollectionPure;
ICollection coll = ...;
IA&IB variable1 = ...;
IA variableA = variable1;// ~upcasttype IEnumerable_Or_ICollectionPure = IEnumerable | ICollectionPure;
IA|IB variable3 = ...;
match(variable3)
{
| a is IA => ...
| b is IB => ...
}
//Возможно
variable3.F();//,если F() есть в обоих интерфейсах (хотя тут подумать надо)
(IA&IB)|IC variable4 = ...;
Здравствуйте, artelk, Вы писали:
A>Нужно только, чтобы можно было объявлять поля с типами таких алиасов, возвращать их из методов и т.п. A>И вообще:
Такие штуки — пересечение метода по имени в разных интерфейсах — придется скорее всего запретить. Или сделать разрешение каким-нибудь дубовым способом. Вероятность коллизии мала, а усложнение языка для обхода таких ситуаций будет весьма значительное.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Неочевидно. Приведенный пример спокойно переписывается без наследования интерфейсов.
Нет, не переписывается в тех сценариях, где требуется зависимость IPet от IAnimal. В отсутствии явной зависимости в этих сценариях нужно будет динамическое приведение типов. В случае наследования интерфейсов — дешевое статическое.
Предложенный способ через генерики — банально более многословен и является эмуляцией того же самомого с тем отличием, что вместо однократной декларации зависимостей ты будешь их объявлять вручную в каждом месте.
ВВ>Ну т.е. наследование нужно для подключения реализации? Неужели это без наследования никак не сделать?
Ну дык интерфейс — это, считай, кортеж ф-ий, составляющих контракт, а this — идентити реализации. Без наследования ты будет точно такую же механику обыгрывать ручками на кортеже функциональных типов. Где смысл?
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Неочевидно. Приведенный пример спокойно переписывается без наследования интерфейсов.
V>В отсутствии явной зависимости в этих сценариях нужно будет динамическое приведение типов.
Нет, динамического никогда не будет.
V>Предложенный способ через генерики — банально более многословен
В C# — да, а вообще можно синтаксис и получше придумать, как мне кажется.
V>вместо однократной декларации зависимостей ты будешь их объявлять вручную в каждом месте.
Алиасы уже тут обсуждались, не обязательно в каждом месте.
ВВ>Ну вообще, если оставить за скобками те же экзистенциалы и полиморфную рекурсию, то в Хаскелле получается как раз наоборот — наследование есть, а динамик диспатча нет.
Есть на классах типов. В такую же точно табличную диспетчеризацию и вырождается в бинарнике, как в мейнстримных языках на vtable.
Здравствуйте, vdimas, Вы писали:
ВВ>>Ну вообще, если оставить за скобками те же экзистенциалы и полиморфную рекурсию, то в Хаскелле получается как раз наоборот — наследование есть, а динамик диспатча нет. V>Есть на классах типов. В такую же точно табличную диспетчеризацию и вырождается в бинарнике, как в мейнстримных языках на vtable.
Выбор инстанса — в компайл тайме. В "такую же точно табличную диспетчеризацию" он вырождаться уж точно никак не может. При наивной компиляции он рассахаривается в dictionary passing style.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Выбор инстанса — в компайл тайме.
Нет. Т.е. не обязательно, в этом весь трюк. Там где возможен выбор в compile-time, там классы типов и нафик не упали.
ВВ>В "такую же точно табличную диспетчеризацию" он вырождаться уж точно никак не может.
???
ВВ>При наивной компиляции он рассахаривается в dictionary passing style.
Не нужен там никакой dictionary. Там для каждого динамического случая нужен кортеж из используемых ф-ий тайпкласса. Поищи, разбирали подробно полиморфизм С++ vs полиморфизм Хаскеля.
Там ближайший аналог — это реализация тайпклассов через фиктивные АлгТД, в которых хранятся ф-ии тайпкласса, но выбор ф-ий по коду АлгТД и последующий их вызов ничем принципиально от виртуальных вызовов не отличается.
Здравствуйте, vdimas, Вы писали:
ВВ>>Выбор инстанса — в компайл тайме. V>Нет. Т.е. не обязательно, в этом весь трюк. Там где возможен выбор в compile-time, там классы типов и нафик не упали.
Он не выбирается только в весьма ограниченных случаях, которые я к тому же перечислил вроде как все. И в остальных случаях классы типов очень даже упали, хотя и выбираются статически.
ВВ>>В "такую же точно табличную диспетчеризацию" он вырождаться уж точно никак не может. V>???
Ну покажи, как точна такая же табличная диспетчеризация будет работать, когда членом класса является константа.
ВВ>>При наивной компиляции он рассахаривается в dictionary passing style. V>Не нужен там никакой dictionary. Там для каждого динамического случая нужен кортеж из используемых ф-ий тайпкласса. Поищи, разбирали подробно полиморфизм С++ vs полиморфизм Хаскеля. V>Там ближайший аналог — это реализация тайпклассов через фиктивные АлгТД, в которых хранятся ф-ии тайпкласса, но выбор ф-ий по коду АлгТД и последующий их вызов ничем принципиально от виртуальных вызовов не отличается.
А ну да, в имитации тайпклассов на С++ используются виртуальные функции, только причем тут Хаскелл?
Тайпклассы Хаскелла это по сути перегрузка функций, которая разрешается во время компиляции.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, PSV100
PSV>>Лично я двумя руками за упрощение. Я не вижу никакого кайфа в иерархии интерфейсов, как таковой. И война с ограничениями системы типов пусть останется для Хаскеля, это его стихия. Проблема в том, что действительно иногда необходимо где-то выразить сущность типа именно как "объединение" (INum IFractional). Имхо, в языках с динамической типизацией, как та же Кложура, возможно, это не так восстребовано. А для Ela есть идеи как указать такие "объединения" ? (сорри, если глупость спрашиваю, я с языком знакомился уже давненько и "по диагонали", и недавно видел здесь инфу про появление "протоколов")
ВВ>Так Ela динамически типизированная.
Ага, действительно. У меня почему-то в памяти отложилось впечатление, что Ela в процессе развития "скатилась" до строгой статики. Я ещё раз повнимательнее глянул описание классов, действительно, фактически, это протоколы кложуры, но диспетчеризация не только по первому аргументу. Хорошо, что для параметра класса нет требований по реализации других классов, т.е. некая хаскелевская иерархия. Имхо, для понимания структурной логики кода вполне достаточно, что функции объединены в смысловые группы (классы).
Удачи в проекте.
Здравствуйте, artelk, Вы писали:
_NN>>Вполне здравая идея отказаться от наследования если система типов это будет позволять. _NN>>Очевидно нужно будет ввести некие алиасы и объединения типов, чтобы не писать весь набор каждый раз. _NN>>Получается по сути почти то же, что и наследование но без сильной связи между интерфейсами.
_NN>>Старый вариант _NN>>
_NN>>P.S. _NN>>Так и к структурной типизации придем
A>Нужно только, чтобы можно было объявлять поля с типами таких алиасов, возвращать их из методов и т.п. A>И вообще: A>
A>type ICollection = IEnumerable & ICollectionPure;
A>ICollection coll = ...;
A>IA&IB variable1 = ...;
A>IA variableA = variable1;// ~upcast
A>type IEnumerable_Or_ICollectionPure = IEnumerable | ICollectionPure;
A>IA|IB variable3 = ...;
A>match(variable3)
A>{
A> | a is IA => ...
A> | b is IB => ...
A>}
A>//Возможно
A>variable3.F();//,если F() есть в обоих интерфейсах (хотя тут подумать надо)
A>(IA&IB)|IC variable4 = ...;
A>
Ceylon: Principal typing, union types, and intersection types
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, artelk, Вы писали:
A>>Нужно только, чтобы можно было объявлять поля с типами таких алиасов, возвращать их из методов и т.п. A>>И вообще:
ARK>Такие штуки — пересечение метода по имени в разных интерфейсах — придется скорее всего запретить. Или сделать разрешение каким-нибудь дубовым способом. Вероятность коллизии мала, а усложнение языка для обхода таких ситуаций будет весьма значительное.
Здесь в теме немного затронули Go, у него как раз есть такие объединения, и для структур тоже (кроме интерфейсов), и "дубовые" правила для конфликтов имён.
Здравствуйте, PSV100, Вы писали:
ВВ>>Так Ela динамически типизированная. PSV>Ага, действительно. У меня почему-то в памяти отложилось впечатление, что Ela в процессе развития "скатилась" до строгой статики.
Боюсь, что прикручивать адекватную систему типов ко всему этому — это уже за пределами возможностей одного человека. Да и все равно язык сильно потеряет в выразительности. Скажем, мой любимый трюк с функциями, тип которых вычисляется в рантайме, будет уже невозможен. А это весьма полезно при создании каких-нибудь DSL-ей — например, для тестов:
test1 =
test "Demonstration of espec"
given [1..5]
should contain 1
should contain 3
shouldn't contain 6
do reverse
should be [5,4..1]
PSV>Я ещё раз повнимательнее глянул описание классов, действительно, фактически, это протоколы кложуры, но диспетчеризация не только по первому аргументу. Хорошо, что для параметра класса нет требований по реализации других классов, т.е. некая хаскелевская иерархия. Имхо, для понимания структурной логики кода вполне достаточно, что функции объединены в смысловые группы (классы). PSV>Удачи в проекте.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Неочевидно. Приведенный пример спокойно переписывается без наследования интерфейсов.
Любой пример без проблем переписывается с помощью инструкций копирования значений типа mov, сравнений и условных переходов. Давай обсудим зачем нужно что-то еще?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Ничего не понял. Зачем мне отнаследованный интерфейс приводить к базовому да еще "безопасно"? Зачем мне вообще приводить интерфейс к чему-либо?
Тебе же уже ответили раз 5 — полиморфизм. В данном случае ООП-ный.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
я об этом пишу более подробно. Изначально, интерфейсы были придуманы, как способ программной абстракции (т.н. поведенческая абстракция). Интерфейс -- это определенный язык (словарь), посредством которого договариваются общаться с программными модулями. Абстракция здесь состоит в том, что производится отвлечение от деталей реализации языка модуля. Т.е. есть интерфейс и его реализация. При этом модули не обязательно рассматриваются как машины состояний. Наследование появилось потом, когда Лисков биологизм в программирование внесла, взяв за идеал таксономию Линнея.