LINUX.ORG.RU

Язык, в котором есть constraints'ы

 constraints, ,


0

4

Это то же самое, что типы данных, но для логики приложения, а не логики исполнителя (CPU).

Пример constraint'а: «constrX is integer, [0..55],[117..200],999»

Другой пример: «constrY is string, regexp(^fo+\s+bar$)»

По-моему по сравнению с ограничениями на внешние данные в духе «вот в этом поле число не должно быть больше 2^32-1» потому что у нас битовая разрядность такая - contraint'ы были бы гиганстким шагом вперёд, позволили бы существенно сократить «проверяющий» код и оптмизировать выполнение за счёт того, что нам не пришлось бы создавать объект только ради того, чтобы убедиться в консистентности значения.

Оптимальным видится такой вариант использования: переменная, заполняемая из внешнего источника -> наложение constraint'ов -> внутренняя переменная с мета-тегом «пройдён constraint такой-то».

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

Например, во всеми нелюбимом perl'е есть уже львиная доля такой логики: а именно taint mode, когда переменные делятся на «грязные» (из внешних источников) и «чистые». Недостаток - как раз в отсутствии простого механизма наложения ограничений, который бы и «очищал» переменную.

Речь о том, что сами по себе типы данных в компилируемых языках - это сказка о повышении производительности, но никак не о повышении стабильности кода. Потому что если в вашу переменную типа UInt8, которая не может быть за диапазоном [101..143] приехало вдруг 235, а вы это не проверили в рантайме - ну как бы тип данных вас точно не спасёт.

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

★★★★★

Возьми любой ооп язык древнее говна мамонта и сделай проверки в конструкторе/фабрике, а потом юзай этот объект везде. В языках с zero cost abstraction вроде С++/Rust это ещё ничего не будет стоить. В Java тоже не дорого, но не будет примитивным типом и будет в куче

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

Рантайм-проверки будут стоить ничего? Ой-вей! И еще есть мнение, что проще if вставить там, где это действительно важно, чем костылить классы на каждый чих (а еще нужно не забывать их использовать, лол)

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

После создания экземпляра класса и превращения непроверенных значений, вроде std::string в my_namespace::phone_number больше ничего проверять никогда не нужно при условии иммутабельности. Это и хочет ТС. То что будет проверено compile time смысла не имеет, так как на чем мы там экономим? На создани пары констант из статических данных? Это полезно только в рантайме. Проверил значение один раз в конструкторе, потом гоняешь валидный экземпляр класса без всяких проверок

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

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

Лол :-) Нагородил целый абзац тарабарщины-отсебятины вместо одного слова - инвариант :-) Слышал о таком? :-) Лол :-)

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

Я просто допустил что ты не слышал. Не хотел запутать

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

да, с интами не очень то удачно. Я как то в первую очередь сторону всяких списков полиморфных начал думать, с тайп-левел длиной. Или хотя бы ограничение на непустой список, чтобы safeHead какой нибудь делать. А инты будут, действительно, требовать вычисления.

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

А что подразумевалось под «менять стандартные классы», не совсем уловил мысль?

PS. Правда, кажется, что компилиться такая котовасия будет нереально долго если какие нибудь большие числа будут. Там ведь придётся наверное придётся натуральные числа вида (S (S (S Z))) использовать, а они довольно медленно вычисляются.

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

похоже есть в хаскеле: запрещаешь экспорт консруктора типов из модуля и експортируешь только функции умеющие из грязных типов создавать чистые

Ты так пишешь, будто то же самое не делается в C++, да и в любой java в каком-то виде.

anonymous
()

ТС, если бы ты не так ненавидел ООП, то понял бы что переизобретаешь его кусок.

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

ну кажется что так, да не совсем. Вот положим есть у нас тип class Int16

вопрос номер 1 - как прибавить к нему скажем ещё один Int16?

Т.е. ведь уже будет Int32. С зависимыми типами, если я верно понимаю, можно получать для каждого значения свой уникальный тип. положим мы складываем 16 и 8 , Типы должны быть соотвественно Int 16 и Int 8 , а результат будет 24 типа Int 24. При этом можно делать проверки что тип входит в диапазон, скажем, < 25. Все эти проверки в компил тайме будут.


а в более «обычных» языках - как такой фокус провернуть? Только в рантайм выходить, или я ошибаюсь?

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

На лету создавать типы наверно никак, только заранее предусмотреть

Int24 operator+(Int16, Int8);

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

Операции в стандартных класса типов, например для сложения выглядят как `(+) :: a -> a ->` в нашем случае это или `(+): a -> b -> c` и фунциональной зависимостями `a,b->c` `b,c->a` `a,c ->b` причем по классу для опреации, т.к. зависимости разные (можно констрейнты вытащить в параметр типа, но там ситуация будет похожая), функциональных зависимостей тоже может быть поменьше, по настроению и желанию.

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

В целом автор спрашивает про констреинты любого вида, как это есть в лиспе, т.е. что вроде (псевдокод) `(constr (lamda x: x^2+x-5<20) mydouble)` и тогда при для вещей с тегом mydouble гарантируется выполнение этого констрейнта, вытаскивать это на typelevel и тем более выводить автоматом достаточно тяжко, поэтому решение со своим типом и умным констркутором выглядит достаточно разумно, а количество проверок будет таким же как и в лиспах.

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

Да вообще зачем нужны системы охранной сигнализации и видеонаблюдения, датчики какие-то, если есть бейсбольная бита и Серый с братанами?

Мы говорим о разных вещах.

Мне нужен ЯП, который реализует фактически фильтр всех входящих данных по заданным декларативно описанным (DSL-языком) правилам.

Вы мне говорите о том, что в ООП всё возможно.

Спасибо, КЭП. Расскажите мне ещё о том, что «вычислительная техника творит чудеса», «развитие ЭВМ придало новый мощный импульс техническому прогрессу» и пр. подобное. Это прекрасно, что в каждлом сеттере можно проверить, что там сетиться, а в каждом геттере можно отдавать значение именно в том формате, который нужен запрашивающему или вообще не отдавать, если запрашивающий не иметт никаких прав доступа к соотв. объекту. Но, извините, даже сеттеры и геттеры наверное в 90% случаев реализованы как прокси-сервис к простой записи/чтению переменной без всяких проверок, что в переменную положили, или какая интерпретация переменной нужна «берущему», я уж не говорю о том, чтобы проверять доступ в геттере. Т.е. это всё можно, но это геморрой, а код, как правило, должен был работать у заказчика позавчера.

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

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

Вы мне говорите о том, что в ООП всё возможно.
Т.е. это всё можно, но это геморрой, а код, как правило, должен был работать у заказчика позавчера.

Вообще-то массовый приход ООП в индустрию — это 1980-е годы (SmallTalk-80 — начало 80-х, С++, Eiffel, ObjectPascal — середина-конец 80-х). Так что у всех, кому ограничения на значения и на типы были реально нужны, код работал должным образом у заказчика даже не позавчера, а 30 лет назад.

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

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

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

Стиль программирования на таких языках изменился с годами достаточно сильно. Многие компании на уровне code style поощряют сильное отделение value class от других классов. Первые еще и иммутабельные обычно и создаются с фабриками, в которых стоят валидаторы. Не зависимо от языка, C++ подобный псевдокод выглядит так

class MyClass {
private:
   MyClass(...)

public:
   MyClass Create(...) {
      Validate(IsLatin(name))
      Validate(0<=age<=150)
      Validate(MatchPhoneNumber(phone_number))
   }

   // NO SETTERS!
}

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

А вот подход с организацией фильтра входящих данных на уровне языка

Необходимости никакой в на уровне языка нету. Ну вот возьми Scala, где есть на уровне языка

case class MyClass(name: String, age:Int) {
   require(IsLatin(name))
   require(0<=age && age<=150)
}

Стало легче? Наверное стало, меньше печатать. Геттеры определены сразу. Сеттеров нету. Есть конструкторы копирования со встроеной мутацией данных (x2=x1.copy(age=10)). Но это просто сокращение бойлерплейта, просто удобство

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

Я все к тому, что

Оптимальным видится такой вариант использования: переменная, заполняемая из внешнего источника -> наложение constraint'ов -> внутренняя переменная с мета-тегом «пройдён constraint такой-то».

все так и делают. Код написаный в компаниях, где следят за стилем кода во врема ревью (не как васян захотел, а ревьювер проверяет что если язык позволяет писать голые типы, то никто не пишет голые типы). Никто не бегает (с голой жопой) с миллисекундами в int64 (или секундами? или микросекундами? или что это вообще такое?), а люди сразу конвертируют это в Time/Duration. Или идут дальше и конвертируют в еще более ограниченый тип, которые прошел еще более жесткую валидацию (например что это последний год) и дальше не парятся и пользуются уже провереным типом.

И Time нельзя сложить с Time. Но можно с Duration. Duration можно сложить или отнять. Time можно отнять от Time и получить Duration. А для процессора это все просто будет int64, из-за подхода zero cost abstraction

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

проверки в конструкторе/фабрике

Мало кто захочет этим страдать. На каждый регексп делать подкласс String - это совсем нужно быть поехавшим. Нужен стандартный String, ограниченный в определенном контексте. Чтобы любые мутации такого объекта проверялись автоматически. А иммутабельность - слишком простой случай, тут можно и без ООП обойтись функцией-конструктором.

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

Код написаный в компаниях, где следят за стилем кода во врема ревью (не как васян захотел, а ревьювер проверяет что если язык позволяет писать голые типы, то никто не пишет голые типы)

Ну понятно, в добавок к компилятору нужен еще мужик с плеткой. Иначе это не работает.

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

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

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

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

И кстати «один класс String для всего» не позволит ограничить проверки компайл-таймом.

Да, но 50 подклассов String, например, это совсем кисло. И нужно еще не забывать постоянно конвертировать входящие строки, что все как раз и норовят сделать. В общем, без мужика с плеткой никуда (и то, может проглядеть).

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

Альтернативы этому нету. Применяется здравое количество проверок компилятором. А как иначе? Вот я пошел, зафигачил где-то String и ожидаю что там номер телефона. Компилятор должен догадаться что я имел ввиду в моем коде? Конечно на границах API будет стоять PhoneNumber класс и туда string без прохождения валидатора не всобачишь. Если я пользуюсь string, а мне нужно вызвать API с PhoneNumber, то без валидации я не обойдусь, это проверит компилятор. Мужик с плеткой нужен только для того, чтобы мы все приложения с нуля не нафигачили со string, с низу до верху. Компилятор тут уж точно не проверит все приложение и не догадается что мы имели ввиду.

На каждый регексп делать подкласс String - это совсем нужно быть поехавшим

Если тебе такое не подходит - не делай, разрешаю. Я лишь отвечал на вопрос ТСа, который утверждает что ему нужно что-то на уровне языка особенное. А я говорю что это уже делается даже в таком недоговноязыке как С++, не говоря уже о что в чем-то нормальном.

Нужен стандартный String, ограниченный в определенном контексте.

Это слова. Что конкретно это значит? Класс PhoneNumber это просто стандартный String (по стоимости для процессора), который ограничен (компилятором) в определенном контексте.

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

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

Вот код

string phone_number.

Почему я так написал? Я, как программист, а не психопат со скальпелем, должен был использовать класс PhoneNumber. Если я ошибся, то мне на первом же вызове API с PhoneNumber компилятор попросит провести конвертацию это будет первым намеком что я должен был это сделать раньше. Я, как программист, а не психопат со скальпелем, быстро исправлю свой код.

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

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

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

Ну вот именно. Приходим к тому, что всю иерархию нужно заранее продумать.

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

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

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

Да, но 50 подклассов String, например, это совсем кисло. И нужно еще не забывать постоянно конвертировать входящие строки, что все как раз и норовят сделать. В общем, без мужика с плеткой никуда (и то, может проглядеть).

Предлагаешь в каждой функции в рантайме проверять, что ей передали именно тот верифицированный стринг, а не какой-то левый?

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

Ну конечно, усложнение рантайма даром не дается.

Зачем усложнять рантайм, если кроме новых багов это ничего не даст?

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

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

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

Предлагаешь в каждой функции в рантайме проверять, что ей передали именно тот верифицированный стринг, а не какой-то левый?

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

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

Хотя, на коленке примерно такое должно сработать:

template<int N> struct Int
{
    int n;
    constexpr Int(int val): n(val) { Expects((n >> N) == 0); }
};

template<int N1, int N2>
auto operator+(Int<N1> n1, Int<N2> n2) -> Int<N1 + N2>
{
    return { n1.n + n2.n };
}

// Складывать размерности тут не очень верно, но не суть

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

Если с позиций типолюбства, вот что я хочу:

YobaString = String /^yoba/
# рантайм-проверки генерятся компилятором. 

Теперь смотри что делает рандомный классолюб:

class YobaString:
  constructor (String val):
    # тут проверка, но я пока не вкурил как делать
    this.val = val
# канпелируется, значит все норм!

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

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

Неа, типы как контракты это как раз из серии когда люди пытаются в динамике переопределять встроенные типы, не подозревая о том, что им на самом деле нужно другое.
Вот простой пример. Я буду использовать любимый тобой Rust. Допустим мы пишем программу, которая оперирует яблоками. То есть нам нужен тип «яблоко». Прежде всего приходит в голову, что яблоки характеризуются количеством. Поэтому мы возьмем для обозначения яблок какой-нибудь числовой тип. Очевидно, что он должен быть целым и беззнаковым. Пусть, это будет u8.
Я хочу написать функцию, которая суммирует одно количество яблок с другим.

fn sum_apples(apples1: u8, apples2: u8) -> u8 {
    apples1 + apples2
}

fn main() {
    println!("{:?}", sum_apples(1, 2));
}

// 3
Потом я внезапно понимаю, что скорее всего обойтись возможностями числового типа не получится, потому что я хочу в дальнейшем сортировать яблоки по цвету. Что делать, чтобы не выдумывать, какой мне нужен тип прямо сейчас? Используем алиасы типов:
type Apple = u8;

fn sum_apples(apples1: Apple, apples2: Apple) -> Apple {
    apples1 + apples2
}

fn main() {
    println!("{:?}", sum_apples(1, 2));
}

// 3

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

#[derive(Debug)]
struct Apple(u8);

impl std::ops::Add for Apple {
    type Output = Apple;

    fn add(self, other: Apple) -> Apple {
        Apple(self.0 + other.0)
    }
}

fn sum_apples(apples1: Apple, apples2: Apple) -> Apple {
    apples1 + apples2
}

fn main() {
    println!("{:?}", sum_apples(Apple(1), Apple(2)));
}

// Apple(3)
Правда, как видно, места использования функции придется поправить. Но компилятор позаботится, чтобы ты не забыл об этом.

Virtuos86 ★★★★★
()
Ответ на: комментарий от bread
YobaString = String /^yoba/
# рантайм-проверки генерятся компилятором.

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

Теперь смотри что делает рандомный классолюб:

Вообще-то так все делают. А как это делают рубицари?

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

Вообще-то так все делают.

Забивают на проверки? Я знал!

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

Ничесе ты бойлерплейта настрочил. И что, всю арифметику будешь для своих яблок велосипедить? Алгебра яблок, лол. Вот же типолюбство как оно есть. Смотри как просто все в SQL: есть базовый констрейнт - тип, а дальше мы его уточняем предикатами и получаем подмножество возможных значений все того же типа. Никакую новую алгебру для этого «подтипа» естественно не нужно переизобретать. Все функции доступны, никаких оберток с бойлерплейтом не нужно. В принципе, наследование позволяет что-то похожее изобразить, хоть и не декларативно. Но в раст как раз наследование не завезли :(

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

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

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

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

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

Вообще-то нужно. Если мы берём некий стандартный тип и добавляем на него ограничения, то старые операции над ним могут перестать иметь смысл. Например, если взять unsigned int и наложить на него ограничение «только простые числа», то непонятно что делать с операцией «умножение» для такого типа. Вариантов может быть несколько:

  • Не давать юзеру умножать такие числа.
  • Сделать функцию, которая всегда кидает исключение. Или сигнализирует ошибку другим способом.
  • Возвращать обычный unsigned int.
  • ...

На самом деле конкретный выбранный вариант тут не важен. Важно то, что при наложении дополнительных ограничений у типа так или иначе меняется интерфейс.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 2)
Ответ на: комментарий от bread

Ничесе ты бойлерплейта настрочил.

Бойлерплейт это писанина кучи кода, который делает минимальную работу. Здесь никакого избыточного кода нет: #[derive(Debug)] нужен, чтобы компилятор автоматически реализовал для типа возможность его отладочной печати (то есть с помощью «{:?}» в форматирующей строке макроса println), impl std::ops::Add for Apple это ручная реализация для типа поддержки операции сложения для его инстансов — очевидно, для произвольного типа компилятор не может сделать этого сам. Наследование бы помогло, да. Но ведь

И что, всю арифметику будешь для своих яблок велосипедить?

мне не нужна вся арифметика для яблок. Мне не нужно их делить друг на друга, умножать, возводить в степень. Я уже сколько раз написал: вводимые тобой типы по определению имеют намеренно суженную область применения по сравнению со встроенными. Твоя условная YobaString никогда не будет в твоей программе использоваться во всех контекстах, в которых может быть использована строка сама по себе. И в этом плане автоматически, в духе наследования, выводить для своего типа все интерфейсы строки, при том, что тебе реально нужны только некоторые из них — это и будет бойлерплейтом, автоматическим, но бойлерплейтом, кучей неиспользуемого кода ради нескольких плюшек.

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

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

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

Если никаких требований в итоге не выявится, ты можешь просто остановиться на стадии использования алиасов типов. При этом не будет никаких накладных расходов, в местах использования sum_apples везде используются литералы целых чисел, — никакой лишней писанины. Единственное, из сигнатуры функции будет понятно, что она складывает «яблоки», а не целые числа, что, КМК, выгоднее для понимания. В общем одни плюсы.

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

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

Речь идёт на 90% только о том, что нельзя доверять внешним данным только на основании того, что они подошли к определённому типу. Необходимо тщательно описывать, что есть такое внешние данные и каким критериям должно удовлетворять то, с чем мы согласимся работать.

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

Нужно проверять внешние данные. Причём ограничения, необходимые для очистки и превращения внешних данных во внутренние - могут иметь характер корреляций, когда одни внешние данные могут принимать определённые значения только при определённых значениях внутренних или внешних переменных: «T может быть >100 только если P > 3000»

Проще говоря. мне лично нужен iptables для разработчиков, когда внутри кода можно просто доверять данным, поскольку к ним прицеплени «знаки качества» виде меток «переменная V подходит под constraint X» .

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

Причём именно функции, в которые будут переданы внешние данные, должны определять, какой набор constraints требуется для переменной V. В одном случае хватит только X, а в другом нужны ещё Y, Z и все-все-все.

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

В каждой функции должно быть определены условия контракта в форме: переменная V, если применимо (если она ведёт свою родословную от любого объекта, помеченного как «недоверенный»), должна подходить под ограничения: [isInteger, evenNumber, safeOrderVolume1]

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

И какие операции ты хочешь определить для множества вот таких прошедших проверку данных? То есть, мы явно говорим о некоторых подмножествах. Эти подмножества будут замкнуты относительно операций? Попробуй начать с минимально достаточного набора непротиворечивых операций.

В общем, взгляни на задачу как математик. Что-нибудь получилось? И следующий шаг - как научить этому компьютер?

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

Тут Scala приводили в пример, она по-моему удобнее подходит к решению задачи...

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

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

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