LINUX.ORG.RU

В чём профит от Eiffel...

 ,


2

4

...и, в частности, от design by contract? Чем эта фича принципиально отличается от ассертов в других языках?

Если я не ошибаюсь, контракты (preconditions и postconditions) проверяются в рантайме и написаны на таком же тьюринг-полном языке, как и остальной код, а значит проблему валидности контрактов можно свести к проблеме останова. Как минимум это должно мешать использовать контракты для верификации вообще чего-либо.

Мой интерес вызван тем, что Бертран Мейер - автор языка Eiffel - возглавляет (возглавлял?) кафедру программной инженерии и верификации программ в СПбНИУ ИТМО (http://sel.ifmo.ru/), и используют они в основном Eiffel.

★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 3)

Ответ на: комментарий от eao197
#include <cstdio>
#include <tuple>
#include <boost/variant.hpp>

using Empty = std::tuple<>;

template <typename Link, typename T>
struct Node_ {
    T x;
    Link prev, next;
};

template <typename T>
using DList = typename boost::make_recursive_variant<Empty, Node_<boost::recursive_variant_, T>>::type;

template <typename T>
using Node = Node_<DList<T>, T>;

int main() {

    Node<int> item{1, Empty{}, Empty{}};

    item.next = Empty{};

    if (boost::get<Node<int>>(&item.next)) puts("fail?");
    if (boost::get<Empty>(&item.next)) puts("ok");

    item.next = Node<int>{2, item, Empty{}};

    if (boost::get<Node<int>>(&item.next)) puts("not fail");
    if (boost::get<Empty>(&item.next)) puts("not ok?");

}
motto
()
Ответ на: комментарий от motto

Чем у вас Empty отличается от None в Optional[T] или Void в Eiffel?

Он же используется в точности для тех же самых целей.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

n-арный конструктор это конструктор (как в C++) с n аргументами.

motto
()
Ответ на: комментарий от eao197

Я забыл сказать о неиспользовании частичных функций. Что вместо — в нормальном языке паттерн-матчинг, в C++ есть static visitor.

motto
()
Ответ на: комментарий от tailgunner

Только вот аналог nullptr невозможно будет разыменовать.

Это уже другой вопрос.

Когда мы говорим о ссылках, у нас есть две ситуации:

1. Нам нужно указать, что ссылка пустая (невалидная, не ссылается ни на что). Эта возможность фундаментальна. Без нее нельзя сделать те же двухсвязные списки в императивных языках.

2. Нам нужно защититься от обращения по пустой ссылке.

Мой поинт в том, что для первой ситуации, будь то сырые указатели или AlgDT, аналог nullptr или Void из Eiffel в языке нужен.

Для второй ситуации без помощи со стороны языка программирования не обойтись. То, что сделали в Eiffel с void safety — это частный случай AlgDT для ссылок. Вполне себе удобный и не поломавший серьезно совместимость версий Eiffel-я.

Бесполезно спорить о том, что если бы язык изначально поддерживал AlgDT с паттерн-матчингом, то необходимости в ссылках со значением Void не было бы. Возможно и не было бы (а если бы и была, это было бы упрятано глубоко в недрах стандартной библиотеки и недоступно простому пользователю). Только все это разговоры из категории «если бы у бабушки...»

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Только вот аналог nullptr невозможно будет разыменовать.

Это уже другой вопрос.

Это главный вопрос.

Нам нужно указать, что ссылка пустая (невалидная, не ссылается ни на что). Эта возможность фундаментальна

Нет. Это именно то, в чем раскаивался Хоар. Ссылка должна иметь _только валидные_ значения. То, что невалидно - это не ссылка.

Нам нужно защититься от обращения по пустой ссылке.

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

Бесполезно спорить о том, что если бы язык изначально поддерживал AlgDT с паттерн-матчингом, то необходимости в ссылках со значением Void не было бы

Зато можно поговорить о причинах и последствиях сознательного исключения union types.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от eao197
None : 1 -> Option[T]
Some : T -> Option[T]

Empty : 1 -> DList[T]
Node : T * DList[T] * DList[T] -> DList[T]

nullptr : 1 -> *T
& : &T -> *T

отличие в правилах композиции. Потом вопрос частичных функций, в третьем случае

* : *T -> &T (hardware exception on nullptr)

в Eiffel это, как я понял, решено. Но суть в том, чтобы определять правила конструирования (которые определяют правила деконструкции в паттерн-матчинге) ADT, а не кодировать его через другой тип и левые значения (* или глобальный null).

motto
()
Ответ на: комментарий от tailgunner

Нет. Это именно то, в чем раскаивался Хоар. Ссылка должна иметь _только валидные_ значения. То, что невалидно - это не ссылка.

Блин, ну сколько можно приводить пример с двухсвязным списком. Там нужно указывать, что нет ссылки на соседний элемент.

В этом случае null для ссылки — это валидное значение. Как, например, 0 в математике для целого числа (хотя у римлян обозначения для нуля не было).

Причем void safety в Eiffel наглядно, на практике показал, что в языке могут быть именно что ссылки, и именно что со значением Void, и что наличие Void не будет приводить к обращению по пустой ссылке.

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

Как по мне, то нифига не избавляемся. Просто начинаем работать с ними чуть иначе.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

В этом случае null для ссылки — это валидное значение

Пока ты исходишь из этой посылки, ты ни Хоара не поймешь, ни квазимото.

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

В этом случае null для ссылки — это валидное значение. Как, например, 0 в математике для целого числа (хотя у римлян обозначения для нуля не было).

Деточка, разыменование NULL pointer вызовет злобного дядьку SIGSEGV, который даст тебе по морде. В случае с некоторыми микроконтроллерами — ребут. А вот если бы у тебя был тип данных, который означает ЯЧЕЙКА ПУСТА — вот тогда было бы всё в порядке.

kirk_johnson ★☆
()
Ответ на: комментарий от tailgunner

Пока ты исходишь из этой посылки, ты ни Хоара не поймешь, ни квазимото.

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

eao197 ★★★★★
()
Ответ на: комментарий от kirk_johnson

Деточка, разыменование NULL pointer вызовет злобного дядьку SIGSEGV, который даст тебе по морде.

Дяденька, расскажите это тем, кто программирует на Eiffel после добавления в него void safety. Там и ссылки остались, и значение void они могут принимать. А вот обращаться по ссылкам со значением void нельзя и это контролируется еще в compile-time.

eao197 ★★★★★
()
Ответ на: комментарий от tailgunner

Просто ты слишком упрямый.

Нет, просто я различаю следующие вещи:

T — объект некоторого типа, со своим набором разрешенных значений и состояний.

x — ссылку на объект типа T. У которой есть всего два разрешенных значения: актуальная ссылка на T и Void/null.

Во многих языках, где есть ссылочные типы, но нет отдельного понятия ссылки, разработчики отождествляют x и T. И рассматривают Void/null как одно из значений для T.

Я не считаю такой подход рациональным. Особенно в контексте разговора об Eiffel-е. Возможно из-за своего бэкграунда.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

T — объект некоторого типа, со своим набором разрешенных значений и состояний.

Так вот ссылка (указатель) - это тоже объект, его разрешенные значения - это адреса существующих объектов. И только они, никаких NULL/nullptr/Void. Это другой подход - не такой, к которому ты привык, не подход Си/Си++/Эйффель. Но это не менее разумный подход, и из него тоже раскручиваются все нужные юзкейсы.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Но это не менее разумный подход, и из него тоже раскручиваются все нужные юзкейсы.

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

Т.е. проблема указания факта на то, что x в данный момент никуда не ссылается, такая же фундаментальная, как в математике работа со значением 0.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

Ну, естественно, семантика двусвязного списка никуда не денется. А вот SIGSEGV от разыменования nullptr пропадут.

проблема указания факта на то, что x в данный момент никуда не ссылается, такая же фундаментальная, как в математике работа со значением 0.

Я давно забыл математику, но nullptr - это не 0, а точка разрыва функции.

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

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

Дело не в этом. Мы, возможно, о разных вещах говорим.

Я о том, что в системе типов желательно иметь отдельно «ссылки» и отдельно optional, который можно использовать не только для указателей.

Просто ваш костыль будет это делать теперь не с голым указателем и nullptr, а со специальным значением optional<ref<T>>(). Которое нужно будет явным образом использовать при «обнулении» ссылки и явным образом проверять при обращении по ссылке.

Если у нас есть pattern matching, то так просто вы ничего не нарушите. Вам система типов не даст обратиться к null.

Что, при всем уважении к ФП/AlgDT/паттерн-матчингу есть те же яйца, только в профиль.

Другие это яйца. Вы на стадии компиляции гарантируете, что никогда в рантайме не сможете обратиться к null-ссылке.

anonymous
()
Ответ на: комментарий от tailgunner

Ну, естественно, семантика двусвязного списка никуда не денется. А вот SIGSEGV от разыменования nullptr пропадут.

В сухом остатке: есть объективная необходимость указывать, что какой-то ссылки не существует, а так же есть необходимость защититься от обращения к ссылке без значения.

Соответственно, решать это можно, как минимум, двумя способами:

1. Сделать void/null валидным значением для ссылок, но на уровне языка контролировать наличие проверок на void/null перед обращением по ссылке.

2. Считать ссылкой только валидную ссылку на объект. Для указания отсутствия валидной ссылки использовать какой-то другой объект (например, None в Optional[T]).

Собственно, я не понимаю, почему первый способ должен считаться костылем.

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

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Если у нас есть pattern matching, то так просто вы ничего не нарушите.

Тема посвящена Eiffel-ю, в котором pattern matching-а не было. И не потребовалось, как показала практика.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

В сухом остатке: есть объективная необходимость указывать, что какой-то ссылки не существует, а так же есть необходимость защититься от обращения к ссылке без значения.

Нет понятия «ссылки не существует».

kirk_johnson ★☆
()
Ответ на: комментарий от eao197

Причем, в случае с C++, компилятор не даст вам по рукам

Это смотря как optional сделать. Можно вот как-то так:

...
struct optional {

...

template<typename Reader, typename NullHandler>
auto match(Reader r, NullHandler h) -> decltype(0 ? r(data()) : h())
{
    if (!is_null()) {
        return r(data());
    }
    else {
        return h();
    }
}

...

};

Использовать, правда, не очень удобно. Но чтож делать, раз нет сопоставления с образцом?..

next.match(
    [](ref<node> n) {
        // node
    },
    []() {
        // null
    }
anonymous
()
Ответ на: комментарий от eao197

1. Сделать void/null валидным значением для ссылок, но на уровне языка контролировать наличие проверок на void/null перед обращением по ссылке.

2. Считать ссылкой только валидную ссылку на объект. Для указания отсутствия валидной ссылки использовать какой-то другой объект (например, None в Optional[T]).

Собственно, я не понимаю, почему первый способ должен считаться костылем.

Потому что в первом случае мы вводим механизм, узко заточенный под ссылки, а во втором - используем механизм pattern matching (или аналогичный оператор работы с tagged unions), полезный для решения гораздо более общих задач.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Потому что в первом случае мы вводим механизм, узко заточенный под ссылки

Не вижу в этом ничего плохого, т.к. при обычном программировании приходится считаться с объективной реальностью. Например, из-за которой в статически-типизированных языках есть разные типы для целых чисел и разные для вещественных. Почему-то то, что int — это одно, а float — другое, воспринимается нормально. А вот то, что есть указатели/ссылки — это уже плохо. Тем более, если рассмотреть указатели на указатели. Таки запись T** несколько удобнее, чем Optional[Optional[T]].

Хотя, если оперировать понятиями теории категорий и разрабатывать модели микропроцессоров на Haskell-е, то AlgDT рулят и бибикают, наверное :)

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от eao197

Собственно, я не понимаю, почему первый способ должен считаться костылем.

Потому что он ведёт к ненужному усложнению языка ещё одним специальным случаем, в отличие от второго способа, который реализуется общими штатными средствами. Т.е. второй способ следует KISS, а первый — нет.

korvin_ ★★★★★
()
Ответ на: комментарий от eao197

Таки запись T** несколько удобнее, чем Optional[Optional[T]].

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

let x :: **T in case x of
  **valOfT -> doSomethingWithVal valOfT
{- *optOfT -> doSomethingWithOpt optOfT -}
  _        -> {- got Nothing -}

Или посмотри, как в том же Хаскелле реализуются «цепочки» вызовов функций, принимающих значение типа, а возвращающих Maybe, например. Без всяких «специальных случаев» и вручную писать проверки не нужно.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от korvin_

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

Ну раз здесь все такие умные, то объясните, почему 30 лет назад языках, которые стали широкоиспользуемыми, ничего подобного не сделали?

Может тому были объективные причины?

И может взгляд ФП-шников на то, как нужно программировать, всего лишь один из возможных?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Потому что в первом случае мы вводим механизм, узко заточенный под ссылки

Не вижу в этом ничего плохого

Ну в костыле нет ничего плохого - он помогает ходить.

при обычном программировании приходится считаться с объективной реальностью

Программирование с ADT - вполне обычное программирование, оно считается с общей для всех нас объективной реальностью.

Хотя, если оперировать понятиями теории категорий и разрабатывать модели микропроцессоров на Haskell-е, то AlgDT рулят и бибикают, наверное :)

Mach7

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

Ну раз здесь все такие умные, то объясните, почему 30 лет назад языках, которые стали широкоиспользуемыми, ничего подобного не сделали?

Потому, что развитие шло от императивного подхода изначально. Потому, что проще сделать. Есть у нас адреса, зарезервируем значение, чтобы показывать «отсутствие ссылки» и все. Языки были довольно простыми. Сейчас растут объемы проектов, тяжело в голове держать код, появляются те или иные приемы. Начинают применяться те приемы, что раньше были невостребованны.

anonymous
()
Ответ на: комментарий от tailgunner

Программирование с ADT - вполне обычное программирование, оно считается с общей для всех нас объективной реальностью.

У каждого, походу, своя реальность. Из языков, которые более-менее близко к мейнстриму, AlgDT и полноценный паттерн-матчинг есть в Scala, Haskell и OCaml.

Во всем остальном, включая C, C++, Java, C#, JavaScript, Python, Ruby, PHP, VisualBasic и т.д., кроме костылей ничего и нет.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Сейчас растут объемы проектов, тяжело в голове держать код, появляются те или иные приемы.

Объемы проектов привысили возможности отдельных разработчиков уже очень и очень давно. Как раз лет 30-40 назад, когда был очередной кризис разработки ПО, и создавались языки вроде C++, Ada и Eiffel. Продолжением которых стали появившиеся чуть позже Java, C# и Scala.

Кроме того, пришествие Web-а и мобильных платформ (смартфонов, планшетов, умных телевизоров и пр.) привело к востребованности таких языков, как PHP, Ruby, Python и JavaScript. И что-то мне подсказывает, что средние размеры проектов на этих языках будут поменьше, чем средние размеры проектов на C++, Ada или Eiffel. А так же Java и C#. Тогда как самих проектах на PHP/Ruby/Python/JavaScript, возможно, будет много больше.

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Объемы проектов привысили возможности отдельных разработчиков уже очень и очень давно.

Крайне голословное утверждение.

Как раз лет 30-40 назад, когда был очередной кризис разработки ПО, и создавались языки вроде C++, Ada и Eiffel.

Ada была создана точно не из-за этого.

пришествие Web-а и мобильных платформ (смартфонов, планшетов, умных телевизоров и пр.) привело к востребованности таких языков, как PHP, Ruby, Python и JavaScript.

К вебу имеют непосредственное отношение только PHP и JS. К мобильным платформам же - ни один из перечисленных. Основная особенность этих языков - низкий порог вхождения. Расцвет веба привёл к необходимости в огромной количестве программистов, зачастую в ущерб качеству. Сечёшь фишку?

Физика в том, что во времена зарождения Eiffel возможности вычислительной техники были совсем другие.

Miranda ( http://en.wikipedia.org/wiki/Miranda_(programming_language) ) появилась на год раньше Eiffel, при этом в ней уже были ADT и Pattern Matching. ML же вообще в 1973 сделали.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Miranda ( http://en.wikipedia.org/wiki/Miranda_(programming_language) ) появилась на год раньше Eiffel, при этом в ней уже были ADT и Pattern Matching. ML же вообще в 1973 сделали.

Молодой человек, вы о чем-нибудь за пределами Haskell и Erlang наслышаны?

То у вас голословными являются утверждения, что размеры программ уже очень давно превысили возможности одного разработчика. То Python и Ruby у вас к Web-у отношения не имеют, а JS к мобильным устройствам...

Упомянутые вами Miranda и ML вряд ли были широко известны даже в узких кругах в те времена. А уж о более-менее заметном использовании и говорить не приходится.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Молодой человек, вы о чем-нибудь за пределами Haskell и Erlang наслышаны?

Конечно.

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

Каких программ? Всех программ?

Miranda и ML вряд ли были широко известны даже в узких кругах в те времена

Потому что ты о них не слышал?

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от eao197

У каждого, походу, своя реальность.

Каждому привычна его часть общей реальности.

Из языков, которые более-менее близко к мейнстриму, AlgDT и полноценный паттерн-матчинг есть в Scala, Haskell и OCaml.

Еще F#, Swift. В Erlang есть pattern matching, не знаю насчет ADT.

Во всем остальном, включая C, C++, Java, C#, JavaScript, Python, Ruby, PHP, VisualBasic и т.д., кроме костылей ничего и нет.

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

tailgunner ★★★★★
()
Ответ на: комментарий от hateyoufeel

Конечно.

Крайне голословное утверждение.

Каких программ? Всех программ?

Очень и очень многих программ.

Потому что ты о них не слышал?

Скорее потому, что никогда живьем видеть их не приходилось. Только встречать упоминания в литературе. Например, о том, что Страуструп ссылается на ML говоря о языках, которые оказали влияние на C++. И о том, что Haskell бы мог не появится, если бы автор Miranda согласился бы сделать Miranda базой для развития нового, современного ФЯ.

eao197 ★★★★★
()
Ответ на: комментарий от tailgunner

Еще F#

Да, про F# забыл, каюсь...

Swift.

Он совсем недавно появился и время, когда он станет полноценным мейнстримом, еще впереди.

В Erlang есть pattern matching, не знаю насчет ADT.

Откуда в Erlang-е AlgDT, если там кроме списков и рекордов до недавнего времени вообще ничего не было. Совсем-совсем недавно в язык map-ы добавили.

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

Можно и так. А можно еще и так: «программирование на языках, для которых не сложно найти нормальную работу».

eao197 ★★★★★
()
Ответ на: комментарий от tailgunner

В Erlang есть pattern matching, не знаю насчет ADT

ADT там тоже есть, но из-за отсутствия статической типизации и наличия символов (как в лиспе) не так часто используются.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Он действительно не прав - в статике можно проверить любые контракты. Просто пишешь бекенд к своему любимому пруверу и все.

ЗЫ: имеются ввиду полноценные пруверы, а не всякие агды с петушками.

anonymous
()
Ответ на: комментарий от hateyoufeel

ADT там тоже есть

Нету их в Эрланге.

anonymous
()
Ответ на: комментарий от true_admin

Через >>=?

Не помню, вроде <?> обычно. Ну, думаю, можно и в монаду обернуть.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от eao197

Ну раз здесь все такие умные, то объясните, почему 30 лет назад языках, которые стали широкоиспользуемыми, ничего подобного не сделали?

Обратная совместимость? Лень? Упрямость? Глупость? Куча вариантов.

korvin_ ★★★★★
()
Ответ на: комментарий от true_admin

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

anonymous
()
Ответ на: комментарий от anonymous

Хорошо, как в статике будет проверятся то что индекс не выходит за границы массива? При условии что индекс вводит юзер.

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.