LINUX.ORG.RU

Не баг, а фича

 


0

2

Какие вещи в программирования изначально появились как эдакая недоработка, но потом приобрела `статус фичи`?

Вот как мне кажется (на достоверность не претендую) например лисп - ребятам просто лень было доделывать. Динамическая типизация - разумеется товарищи типа Гвидо и Мацумото знают о преимуществах статической - но её просто сложно сделать, `и так пойдёт`.

Что ещё есть?

Перемещено JB из talks

★★★★★

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

Ответ на: комментарий от nonimous

прочитай на первой странице, про то, что в динамических языках типов в математическом понимании нет - есть прилепляемые к значениям в рантайме теги

Типы в математическом понятии там есть. И пишутся в комментариях :-) Но эти типы не приходится ограничивать подмножеством, которое умеет верифицировать компилятор.

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

И пишутся в комментариях
Но эти типы не приходится ограничивать подмножеством, которое умеет верифицировать компилятор.

Здорово. Лисперы и программы пишут, наверно, в комментариях (на ЛОРе), на языке, не ограниченном возможностями компилятора?

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

Ещё синтаксис для методов расширения в D. Для реализации вызова у объектов методов компилятор просто передавал значение перед точкой первым аргументом, т.е. «func(arg1, arg2, arg3)» и «arg1.func(arg2, arg3)» было абсолютно эквивалентно. Бага прижилась...

А это какие-то реальные проблемы доставляет?

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

Понадобилось 50 лет, чтобы с выходом Rust 1.0 наконец-то профиксить эту багу.

Разве её пофиксили только в расте?

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

В языках со статической типизацией приходится извращаться

Не в языках со статической типизацией, а в языках без алгебраических типов данных. Например, Result(T, Error) в расте отлично работает.

или после операции проверяй флаг ошибки (Си++).

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

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

Не верится, что это могло быть багом.

Ну первый пост, конечно троллинг.

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

Но это ещё почему? Только если брать за пример статической типизации не какой-нибудь С, а язык с выводом типов и т.д.

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

Это ведь тоже не совсем так. Да, не надо думать про byte/short/int/long, но про «более высокоуровневые» типы всё равно думать приходится. Ведь ты выбираешь, что у тебя будет - строка, число или словарь, например.

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

Лисперы и программы пишут, наверно, в комментариях (на ЛОРе), на языке, не ограниченном возможностями компилятора

Я пример функции привёл, которую нельзя написать в статическом языке (map из Common Lisp).

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

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

Фактически, на динамическом языке можно просто писать алгоритм как есть

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

в статических языках сначала надо втиснуть его в прокрустово ложе типизации

Какой пафос.

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

Упс. Success использовать нельзя. Он уже есть в другом типе.

Это, опять же, зависит от языка. Скажем, в расте мы вполне можем использовать одинаковые имена в следующих случаях:

enum A {
    Success,
    E1,
    E2,
}

enum B {
    Success,
    E1,
    E2,
}

Во вторых, есть тип Result<T, E>, так что разные «Success of» плодить не надо. Способ более-менее удобно преобразовывать одни ошибки в другие тоже есть.

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

И заставляет обрабатывать ошибки даже там, где их уже быть не может. Например, для calc мне нельзя написать calc(3) + calc(4), а надо писать

Если ты уверен, что ошибки не может быть, то пиши так:

calc(3).unwrap() + calc(4).unwrap()
Но вообще пример так себе. Ведь если ты уверен, что тебе не нужны проверки - то зови сразу real-calc.

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

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

Ну я же тебе привёл пример.

map.

Ограничение на типы операндов:

первый аргумент — тип последовательности,

второй — функция

далее произвольное количество последовательностей с одинаковым количеством элементов

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

На динамическом языке реализация пишется тривиально. На Haskell — почти никак.

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

Это, опять же, зависит от языка.

Да. Те аргументы в основном против *ML и прочих «автовыводящих типы» языков. Rust, и в принципе C++ страдает только от необходимости писать типы на каждой переменной и функции. Но есть auto и в документации всё равно приходится писать тип.

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

А вот в языках, где есть такое, меня всегда интересовало, как определить тип Int32, например. Потому как весь внешний интерфейс (поля файлов, сетевых протоколов) привязан к битности. А в языке у меня только Integer и Natural. Что в задаче вычисления IP адреса не намного лучше, чем лисповый t.

В хацкелле Int32 и Word32 из коробки. Но, вообще, работать с IP-адресами как с целыми числами не слишком правильно, потому как операции сложения/вычитания/т.д. для них не определены и являются бессмысленными.

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

В хацкелле Int32 и Word32 из коробки.

Ну да. Только

Prelude Data.Int> let a = 3 :: Int32
Prelude Data.Int> let b = 8 :: Int8
Prelude Data.Int> a + b

<interactive>:9:5:
    Couldn't match expected type `Int32' with actual type `Int8'
    In the second argument of `(+)', namely `b'
    In the expression: a + b
    In an equation for `it': it = a + b

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

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

Да ну? Количество адресов в подсети — операция вычитания. Начало следующей подсети класса C — сложения.

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

Rust, и в принципе C++ страдает только от необходимости писать типы на каждой переменной и функции.

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

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

А так вроде бы нет.

Просто в С++ эту «багу» тоже пропихнуть хотят. В расте, в сигнатуре this (вернее, self) присутствует явно, а вызывать тоже можно обоими способами. Вроде, проблем особых и нет.

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

избежать автоматической генерации большого объёма кода

И сделать ручной костыль для этой самой генерации?

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

почему скобочки/буковки влияющие на выполнение это норм, а пробелы это плохо.

Для любителей значимых пробелов теперь есть новый язык:

1 + 3 * 4 
// is still parsed as 1 + (3 * 4), but 
1+3 * 4 
// is parsed as (1+3) * 4:

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

Всё правильно, это числа разных типов.

Да ну? Количество адресов в подсети — операция вычитания. Начало следующей подсети класса C — сложения.

Ну вот у меня есть два валидных адреса: 192.168.1.1 и 192.168.3.1. Если я из первого второй, я получу валидный адрес?

hateyoufeel ★★★★★
()

кстати кто скажет в каких языках можно писать
5 = 4 or 6 or 7
вместо 5 = 4 or 5 = 6 or 5 = 7

что-то я такое припоминаю что где-то обещали что-то подобное

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

На всякий случай уточню.

Такую функцию:

void repeat(int n, void delegate () func) {
    for (int i = 0; i < n; i++) {
        func();
    }
}

Можно вызвать так:

5.repeat({writeln("Hi!")});

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

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

для нормальной функции тип напиши
Я пример функции привёл, которую нельзя написать в статическом языке
На динамическом языке реализация пишется тривиально. На Haskell — почти никак.

Успокойся, вот написал: http://is.gd/C3wvHA

На практике, конечно, все просто напишут .map().collect(), но если тебе захотелось функцию, то вот функция. Переменного числа аргументов в нормальных языках нет, можно либо через векторы мудрить, но зачем, когда есть макросы. Так вполне себе аналог, кроме одного принципиального различия - все типы ресолвятся в компайлтайме, ошибки типизации ловятся сразу, и генерируемый код оптимизируется под текущий тип.

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

А если поменять аргументы местами, то можно будет

{writeln("Hi!")}.repeat(5);

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

Ты тоже не узнаешь, представь себе.

Почему это? В каком-нибудь расте, если в enum с ошибками добавится ещё один тип, то компиляция поломается и потребует обработать новую ошибку.

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

Вот только нигде так не сделано ни для ввода-вывода

Да ладно? В расте есть io::Error специально под это и в сигнатурах он тоже есть.

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

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

Ну я же тебе привёл пример.
map.

И я тебе привел - '+'.

Ограничение на типы операндов:

первый аргумент — тип последовательности,

второй — функция

Ты и сам знаешь, что ограничения не такие.

На динамическом языке реализация пишется тривиально. На Haskell — почти никак.

А с '+' на Си++ это пишется тривиально, а на CL - почти никак.

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

К тому, что здесь заведомо нет ошибки. А статический язык её заставит обрабатывать.

Ну справедливости ради - чаще ведь значения для calc будут приходить откуда-то и их надо будет проверять.

С try чуть лучше, но, во-первых он есть и в динамических языках, во вторых не обязывает проверять ошибку

Это ещё почему? try просто сделает ранний выход из функции и ошибку надо будет проверять тому, кто эту функцию вызвал.

Если же она внутренняя для проекта, то найти все места её вызова — не проблема.

Очень удобно, да.

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

Звёздочка как указатель, как разыменование указателя,

Неужели с двумя спец-символами было бы удобнее?

Необязательно виртуальные деструкторы.

Не баг.

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

Мне для нормальной функции тип напиши.

fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B

Ну да, делает она немного другое, но то, что ты хочешь сделать проблем нет.

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

Ну да. Только

А почему это плохо? С/С++ не пинал только ленивый за то, что там типы легко и неявно приводятся друг к другу.

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

Переменного числа аргументов в нормальных языках нет,

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

но зачем, когда есть макросы

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

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

Ну да, я примерно о том же, только имел в виду не FORTH, а unlambda и Iota.

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

Ну вот у меня есть два валидных адреса: 192.168.1.1 и 192.168.3.1. Если я из первого второй, я получу валидный адрес?

Если ты вычтешь из второго первый, то получишь валидное число адресов.

Аналогично арифметике указателей.

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

Спорный тезис.

Я не вижу, как переменное число аргументов может сосуществовать с нормальной системой типов. В тех редких случаях, когда это действительно нужно, можно использовать макросы. Максимум, что я вижу в расте - дефолтные значения аргументов, а-ля С++.

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

Для решения тривиальной задачи я бы просто сделал .zip.map.collect, задача была сделать близки аналог лишповой функции, иначе monk бы не успокоился со своим «невозможно написать». Так-то я согласен, что максрос тут ни к чему.

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

Если ты вычтешь из второго первый, то получишь валидное число адресов.

Ладно, что насчёт умножения? Целочисленного деления?

Я для Haskell как-то раз думал реализовать instance Num IPv[46] для одного проекта, но решил, что геморроя с этим гораздо больше чем пользы.

Кстати, для количества адресов лучше всё таки использовать маску подсети.

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

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

Можешь переменным числом аргументов считать любую коллекцию.

но зачем, когда есть макросы

Макрос нельзя сохранить в переменной или передать аргументом в функцию.

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

$x$(.zip($xs))* — это во что превращается? Например, для (1..4), (5..8).

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

Я не вижу, как переменное число аргументов может сосуществовать с нормальной системой типов.

Функция max тоже принимает только два аргумента? И надо писать max(x, max(y, max(z, t))) ?

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

Средусить

Ну вот для этого и переменное число аргументов. По факту его можно считать списком (если однородный) или кортежом (если неоднородный)

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

Я не вижу, как переменное число аргументов может сосуществовать с нормальной системой типов.

А в чём проблема-то? Вон в С++ есть std::initializer_list. А в расте достаточно просто какого-то сахара для слайсов.

Максимум, что я вижу в расте - дефолтные значения аргументов, а-ля С++.

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

Для решения тривиальной задачи я бы просто сделал...

Да нет, я именно про переменное количество аргументов, в общем случае.

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

Функция max тоже принимает только два аргумента?

Функция - да, но в расте принято работать с итераторами и дня них есть свой max.

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