LINUX.ORG.RU

Зачем нужна статическая типизация?, или Вы всё врете!

 ,


1

4

В теме "Питонячьи радости " на последних страницах между мной и @rtxtxtrx внезапно разгорелся спор, из которого я понял, что есть еще люди, которые не считают динамическую типизацию (в том виде, в котором она представлена в Питоне, а именно строгая динамическая типизация) серьезным недостатком при работе с большим объемом кода, особенно при рефакторинге. Вообще изначально разговор завязался вокруг назначения type hints введенных в Питон 3: я утверждал, что они нужны для создания семантических связей в коде, которые будут препятствовать внесению деструктивных изменений в код в результате опечатки или иной ошибки кодера (изменил код, в результате которого какое-либо выражение получило некорректное значение, которое тем не менее обладает схожим с корректным значением типовым контрактом, поэтому при запуске код не «упадет» сразу, указав на проблему); оппонент заявил, что они нужны для (само)документации и не более того.
Но потом выяснилось, что и царь-то ненастоящий (читай, статическая типизация). Не нужна она, просто именуй сущности понятно и уповай на строгую типизацию. А если типизация не строгая, то сами виноваты, у нас в Питоне всё ОК.
Поскольку тема большая и вкусная, я предлагаю всем обсудить этот очень важный вопрос в меру скромных сил и познаний каждого желающего. Обсуждение вторичных вопросов, как-то «статическая типизация нужна для генерации эффективного кода», «при динамической типизации тип только один, object» etc. не предусмотрено — спорим только о том, дает ли статическая типизация выигрыш, если надо перекраивать несметные тыщи kloc. Если есть вообще о чем спорить 😅.

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

статья(первоисточник) лишь буквально о гоуто - в целом оно в том числе что же не так в ООП

из статьи абзац:

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

ООП победило в индустрии при участии GUI ибо модель владения виджетами с горем пополам но совместима с иерархиями предметной области - в частности множественное наследование так и не пошло в неумытые массы из -за того что в тот момент не оказалось общеочевидной модели с которой все постоянно сталкиваются как с gui-хайпом и переходом от tui к gui и прочей свисто-звенящей мультимедииТМ

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

Если мы говорим о чем-то реальном а ля хаскель, то напрямую не получится.

Но если говорить на уровне концепции, то код внутри блока try:

try {
  y = a / x;
  z = b / x;
} catch (DivideByZero e) {
  print("Всё пропало, Михалыч!");
}

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

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

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

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

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

у нас есть такие приборы! но мы вам о них не расскажем (с)

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

что можно сделать сегодня, сейчас, из того что есть, а не когда-нибудь и чем-нибудь?

начнем с простого, контракт.

...
Contract<MyValue> contract = Contracts
                                 .verify((value)->{value == 0}, "а не надо на ноль делить!")
                                 .verify(isBadDay(FULLMOON, FRIDAY, 13), "ты выбрал не тот день")
                                 .verify(isBoobenUsed(), "ты забыл постучать в бубен, так дела не делаются")
                                 .onViolated(MySystem::recover)
                                 .onAccepted((value)->{logger.trace("value {} accepted", value}))
                                 .build();

где-то, где необходимо, когда-то, когда нам удобно

...
Immutable<MyValue> myValue = contract.check(/*MyValue*/ some);
...
Immutable<MyValue> anotherValue = contract.check(another);

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

...
doSmth(x, myValue.get());
...
doSmth(y, anotherValue.get());
...
doSmthAnother(myValue.get(), anotherValue.get(), param);

приблизительно так

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

Гуманитарное знание вообще не знание, а словоблудие

разве это хорошо, что правят гуманитарии,

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

разве это хорошо

Конечно это хорошо. Патриции должны заседать в сенате. Кухарки — готовить еду. Крестьяне — пахать землю. Гуманитарии — править миром. Технари — делать разные полезные и прикольные штуки. Каждому своё место.

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

оно(СП) было в тренде лет 20

Я это вижу иначе. Структурное программирование оказалось настолько успешным, что в массовом сознании подменило программирование вообще. Никто не структурирует свои программы, чтобы избежать использования goto, потому что никому и в голову не приходит строить управление на основе goto. Это былинная победа.

ugoday ★★★★★
()

Цитата для размышлений в рамках дискуссии)

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

Подобные заявления — отличительная способность Уорда. Вы читаете их, киваете головой и переходите к следующей теме. Это звучит настолько разумно, настолько очевидно, что не выглядит чем-то глубоким и мудрым. Вроде бы все само собой разумеется. Но давайте присмотримся повнимательнее.

«…примерно то, что вы ожидали». Когда вы в последний раз видели модуль, который делал примерно то, что вы ожидали? Почему попадающиеся нам модули выглядят сложными, запутанными, приводят в замешательство? Разве они не нарушают это правило? Как часто вы безуспешно пытались понять логику всей системы и проследить ее в том модуле, который вы сейчас читаете? Когда в последний раз при чтении кода вы кивали головой так, как при очевидном заявлении Уорда?

Уорд считает, что чтение чистого кода вас совершенно не удивит. В самом деле, оно даже не потребует от вас особых усилий. Вы читаете код, и он делает примерно то, что вы ожидали. Чистый код очевиден, прост и привлекателен. Каждый модуль создает условия для следующего. Каждый модуль показывает, как будет написан следующий модуль. Чистые программы написаны настолько хорошо, что вы этого даже не замечаете. Благодаря автору код выглядит до смешного простым, как и все действительно выдающиеся творения.

А как насчет представления Уорда о красоте? Все мы жаловались на языки, не предназначенные для решения наших задач. Однако утверждение Уорда возлагает ответственность на нас. Он говорит, что при чтении красивого кода язык кажется созданным для решения конкретной задачи! Следовательно, мы сами должны позаботиться о том, чтобы язык казался простым! Языковые фанатики, задумайтесь! Не язык делает программы простыми. Программа выглядит простой благодаря работе программиста!

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

никому и в голову не приходит строить управление на основе goto.

ээээ… linux kernel?

...
void* smth = malloc(...);
...
int abcd = doit(smth);
if(null == abcd) goto cleanup;
...
cleanup:
   free(smth);
return;
olelookoe ★★★
()
Ответ на: комментарий от olelookoe

Это староверы.

Нынешние сделают так:

void* smth = malloc(...);
real_body(smth);
free(smth);
return

...

void real_body(void *smth) 
{
  ...
  int abcd = doit(smth);
  if(null == abcd) return;
  ...
}
monk ★★★★★
()
Ответ на: комментарий от monk

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

В этом случае старый добрый goto cleanup; выручает как и раньше.

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

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

не воспринимайте крупных теоретиков буквально. goto совершенно легален в определенных ситуациях. вот для эмуляции обязательного кода завершения блока он легален абсолютно… ну или надо в язык вводить defer какой-нить.

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

goto абсолютно легален и в ряде случае делает код чище и нагляднее.

Одна среди причин выбора Си вместо Си++ для работы над кодом может быть как раз в том, что код делает ровно-то, что написано дословно. Не содержит скрытой логики произвольного объема сложности.

Это касается и освобождения ресурсов в том числе.

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

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

то есть с помощью goto вы эмулируете работу компилятора для оформления данного вида кода.

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

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

Также как блок между аллокацией ресурса и меткой.

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

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

Позволю себе аналогию. Раньше была штука, называлась бритва. А потом придумали другую штуку для той же цели и чтобы отличать её, дали дополнительный квалификатор — безопасная бритва. А потом вторая как-то вытеснила первую и уже «просто бритва» означает безопасную, а для оригинального устройства нужно вводить уточнение — опасная бритва. При этом опасные бритвы иногда всё ещё используются отдельными эстетами, профессиональными брадобреями или в театре и кино, когда про старину снимают.

Вот то же самое и с трансмутацией программирование/структурное программирование в программирование/всякие извращения в специальных случаях.

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

Блин, вспомнил, как кодогенератор писал. Так, у меня есть работа более практичная! Писать компилятор - это хуже наркомании…

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

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

Имена переменных задать можно всегда. А аллокацию тогда можно делать и в real_body.

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

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

Там обычно switch.

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

И передавать туда структуру, либо ворох аргументов?

Это уже становится устрашающим. Простое решение с goto позволяет сохранить читабельность кода.

Ну трейд-оффы вполне понятны: случайно поставленный return может всё сломать.

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

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

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

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

С ООП такая же история. Никто уже не строит программы в терминах процедур, оперирующих примитивными типами данных.

Теперь программы строят в понятиях типов и их соотношений, а также методов, определённых на этих типах.

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

Ну трейд-оффы вполне понятны: случайно поставленный return может всё сломать.

Кстати, это решаемо.

int foo()
{
   #define return RETURN_NOT_ALLOWED
   int status;
   MyType * x = (MyType *) malloc(sizeof(MyType));
   lock(global_lock);
   .
   .
   .
cleanup:
   unlock(global_lock);
   free(x);
   #undef return
   return status;
}
wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 2)
Ответ на: комментарий от wandrien

И передавать туда структуру, либо ворох аргументов?

Структуру. Разве не проще читать

resources res;
do_smth(res);
free_res(smth);

чем анализировать портянку goto на тему того, что не пропустил ни один ресурс?

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

чем анализировать портянку goto на тему того, что не пропустил ни один ресурс?

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

Никакой «портянки» нет, вы сражаетесь с несуществующей проблемой.

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

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

Список полей в resources однозначно определяет доступные ресурсы для do_smth и free_res.

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

Не все имена являются ресурсами.

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

анализировать портянку goto

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 != NULL ){

       if( isValid(var1) ){
            retval = some_function(var1);
       } else {
          if( isGood(var1) ){
               retval = 0;
          }
       }
       free(var1);

    } else {
       retval = 0;
    }

    return retval;
}

vs

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 == NULL ){
        retval = 0;
        goto out;
    }

    if( isValid(var1) ){
         retval = some_function(var1);
         goto out_free;
    }

    if( isGood(var1) ){
         retval = 0;
         goto out_free;
    }

out_free:
    free(var1);
out:
    return retval;
}

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

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

Я уже вижу следующий шаг в этой цепочке:

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

А началось всё с goto.

Иногда банан - это просто банан.

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

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

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

vs

int inner(const char * param, char ** pvar1)
{
    *pvar1 = fcn_that_returns_dynamically_allocated_string(param);
    char * var1 = *pvar1;
    if(var1 == NULL)  return 0;
    if(isValid(var1)) return some_function(var1);
    if(isGood(var1))  return 0;
    return 1;
}

int function(const char * param)
{
    char * var1 = NULL;
    int retval = inner(param, &var1);
    free(var1); // NULL допустим
    return retval;
}
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

ответ от одного из разработчиков ядра )

(не ну а чо, пусть тоже поучаствуют в дискуссии)

From: Robert Love
Subject: Re: any chance of 2.6.0-test*?
Date: 	12 Jan 2003 17:58:06 -0500

On Sun, 2003-01-12 at 17:22, [redacted by request 12/3/2020] wrote:

> I say "please don't use goto" and instead have a "cleanup_lock" function
> and add that before all the return statements..  It should not be a
> burden.  Yes, it's asking the developer to work a little harder, but the
> end result is better code.

No, it is gross and it bloats the kernel.  It inlines a bunch of junk
for N error paths, as opposed to having the exit code once at the end. 
Cache footprint is key and you just killed it.

Nor is it easier to read.

As a final argument, it does not let us cleanly do the usual stack-esque
wind and unwind, i.e.

	do A
	if (error)
		goto out_a;
	do B
	if (error)
		goto out_b;
	do C
	if (error)
		goto out_c;
	goto out;
	out_c:
	undo C
	out_b:
	undo B:
	out_a:
	undo A
	out:
	return ret;

Now stop this.

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

Почти ассемблер, однако.

do A
undo_a = 1;
if (!error) {
  do B
  undo_b  = 1;
} 
if (!error) {
  do C
  undo_c  = 1;
}
if(error) {
  if(undo_c) undo C;
  if(undo_b) undo B;
  if(undo_a) undo A;
}

Строк столько же, но читать намного легче.

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

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

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

не без этого

все теперь как мещанин во дворянстве структурно прогают. так

при этом пёрлы евангелистов сп сопоставимы с жемчужинами текущих собеседников в сём треде ибо люди всё теже

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

в школе матан был одним, в иституте - другой

Как выпускник мат-меха, 15 лет преподававший математику в старших классах, сообщаю, что матан везде одинаков, различается глубина погружения и количество доказываемого. Формула Ньютона-Лейбница одна и та же, только в школе она выдаётся, а в институте выводится.

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

Если гуманитарное знание даёт власть над миром

Никакое знание не даёт власть. Гуманитарии лишь умеют нравиться электорату, который в среднем вообще никакого знания не имеет.

Гуманитарии — править миром.

Зелёный переход энергетики вам в… могилку. Две Мировые войны. Столетнюю войну, Тридцатилетнюю войну, войну Алой и Белой Розы… Полтора тысячелетия гуманитарного мракобесия в Европе, что звалось Тёмными веками. Конфуцианство Китая, что на 2 тыс стагнировало экономическое, политическое и даже гуманитарное развитие. Кастовая система Индии, опять 2 тысячелетия стагнации.

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

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

P. S. И через точку можно провести несколько прямых параллельных данной.

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

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

Объясняю неучам, в школе, как и везде, утверждается, что в множестве ВЕЩЕСТВЕННЫХ чисел нет корня ЧЁТНОЙ степени из отрицательного числа (если дискриминант квадратного уравнения отрицателен, то ответ: «в множестве действительных чисел решений нет»). Ну а деления на нуль НИГДЕ нет, ни в школе, ни в ВУЗе.

P.S. Мозги с возможностью раскрывать неопределённости 0/0 при нахождении предела ФУНКЦИИ не ипём, исчисление бесконечно малых вообще не АРИФМЕТИКА, а операция нахождения предела вовсе не арифметическая операция деления.

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

в школе, как и везде, утверждается, что в множестве ВЕЩЕСТВЕННЫХ чисел нет корня ЧЁТНОЙ степени из отрицательного числа

Покажешь в учебнике про вещественные или действительные числа?

Например, вот за 8 класс:

«Квадратный корень из числа −100 не существует, так как квадрат любого числа есть число неотрицательное.» (https://adukar.com/images/photo/algebra_8kl_arefieva_rus_2018.pdf)

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

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

Можно. И что? От этого школьная аксиоматика Гильберта хоть на йоту иная, нежели она же в ВУЗе?!

Я больше скажу. Может быть и так, что параллельных прямых вообще не существует. И чё?

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

Школьная аксиоматика утверждается как единственная.

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

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

Покажешь в учебнике про вещественные или действительные числа?

Это одно и тоже. А так, берёшь этот учебник и читаешь НАЗВАНИЕ ПЕРВОЙ ГЛАВЫ:

Глава 1 **КВАДРАТНЫЕ КОРНИ И ИХ СВОЙСТВА. ДЕЙСТВИТЕЛЬНЫЕ ЧИСЛА

Так же изначально речь шла не об алгебре, а о… матане, который проходиться в 10-11 классах и там уже есть даже комплексные числа (см. классический для РФ учебник Алимова/Калягина), хотя их проходят лишь по углублённой программе.

Так же укажу, что АРИФМЕТИЧЕСКОГО корня (а их в 8м классе и изучают) из отрицательных чисел нет и в высшей математике, поскольку ПО ОПРЕДЕЛЕНИЮ, арифметическим корнем (n-ной степени) называется НЕОТРИЦАТЕЛЬНОЕ число, которое в n-ной степени даёт НЕОТРИЦАТЕЛЬНОЕ число бла-бла-бла. Т.е. арифметический корень определён лишь для неотрицательных чисел. Отсюда и утверждение, что (арифметических) корней из отрицательных чисел нет.

mister_VA ★★
()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)