LINUX.ORG.RU

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

 ,


1

4

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

★★★★★

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

Приведите пример.

Если речь о том, что все переменные Variable и в целом ЯП динамический, то «да».
На уровне же синтаксиса её не видно.

При выполнении кода безусловно проверки типов имеются, а в синтаксисе «пиши как угодно» (об этом речь была).

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

(if (= t 0) 1 t)

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

И насколько сложные ограничения он может так рассчитать?

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

А вот где необходимо «ублажать компилятор»:

(: foo2 (-> Not0 Not0 Not0))
(define (foo2 a b) (* a b))

Type Checker: type mismatch
  expected: Not0
  given: Exact-Rational

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

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

И насколько сложные ограничения он может так рассчитать?

Любые, которые определены в системе типов. То есть для чисел это больше/меньше/равно 0. Больше/меньше границ Fixnum, Byte. Для остального любые комбинации предикатов типов (string?, list? vector?, …).

monk ★★★★★
()

Это цена удобству легко кодить сложные штуки, чтоб не кодить сложные штуки сложно. Проблема не в том что эти штуки кодить сложно/просто, а в том что штуки, когда у тебя типизация вылезает где-то в динамике (например на основе конфигов из json-ов, про которые ты почти ничего не знаешь заранее, генерировать какие-то объекты с которыми ещё и работать надо) и встаёт как проблема, относительно сложные. Хорошим примером такой проблемы будет CDDA и код на плюсах которым игра считывает все игровые объекты которые описаны в этом самом json-е. Не то чтобы рокет саенс, но чертовски неприятно, на том же питоне с его типизацией такая задача решается на порядок проще и потратя те же усилия на разработку можно было бы более гибко читать json-ы и вынести больше API для модов туда, а не пилить отдельный проект с названием jsonformatter который будет написанные человеком json-ы приводить к виду с которым C++ программист справится.

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

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

Вы в первом сообщении спрашиваете:

Какие конкретно возможности проектирования?

А во втором отрицаете всякую необходимость расширения возможности языка:

И зачем нужно «расширять концепцию»?

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

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

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

Или ответов нет?

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

скажите, каких концепций вам не хватает, товарищ! будем их обсуждать.

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

Вот выше один из участников разговора демонстрирует примеры на Typed Racket. Сразу понятно становится - про потенциальные возможности, про их применение.

У вас же вместе с crutch_master напару только «старое ооп кококо мозги заплыли у стариков кококо можно на жс расширить концепции кококо».

И после этого хотите, чтобы с вами разговаривали как с нормальным. Не надо так. (c)

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

Это поможет проектировать программы? Чтобы что?

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

b = add_tooltip(add_border(button)).new()

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

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

Вспомнил, что меня еще сразу беспокоило в этом примере с Not0.

Вот в таком виде это обеспечивает сходимость типа, но не обеспечивает банальную арифметическую верность вычитания:

(define (foo a b)
  (define t (- a b))
  (if (= t 0) 1 t))

И если у нас была проблема в том, что мы хотели избежать ран-тайм броска исключения / возвращения кода ошибки / возвращения Optional в делении, то здесь мы просто приходим к тому, что нам нужно это самое {бросок исключения / возвращение кода ошибки / возвращение Optional} кодировать в арифметических операциях над Not0. Во всех.

От чего ушли, к тому и пришли.

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

b = add_tooltip(add_border(button)).new()

Такой пример будет семантически эквивалентен порождению класса через множественное наследование.

Тогда уместен вопрос:

Множественное наследование признано плохой практикой в ООП. Является ли оно тут хорошей практикой?

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

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

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

Возможность статической типизации не помешала бы во всех языках.

В любом языке с динамической типизацией можно писать а-ля Паскаль.

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

В любом языке с динамической типизацией можно писать а-ля Паскаль.

Вообще никакой связи.

Или у вас представления о статической типизации остались на уровне Паскаля?

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

Например, что гуманитарное знание — это высшее знание.

Гуманитарное знание вообще не знание, а словоблудие. Социальное – ещё туда-сюда.

Гуманитарии правят миром

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

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

Гуманитарное знание вообще не знание

Ты мне даже на вопрос не ответишь, что такое «знание».
Все твои так называемые «аксиомы» - не истины.
Чтобы ты понял я приведу пример: в школе матан был одним, в иституте - другой с криками «забудьте школу» и так далее.

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

И если у нас была проблема в том, что мы хотели избежать ран-тайм броска исключения / возвращения кода ошибки / возвращения Optional в делении, то здесь мы просто приходим к тому, что нам нужно это самое {бросок исключения / возвращение кода ошибки / возвращение Optional} кодировать в арифметических операциях над Not0. Во всех. От чего ушли, к тому и пришли.

Ну и собственно потому исходная задача не решена.

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

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

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

В выражении вида x / constexpr(y) есть три очевидных исхода компиляции:

  • Компилятор вычисляет y в константу, и она не равна нулю - программа компилируется.
  • Компилятор вычисляет y в константу, и она равна нулю - программа не компилируется, поскольку деление на нулевую константу диагностируется при компиляции.
  • Компилятор не может вычислить y в константу - программа не компилируется, поскольку не выполнено требование constexpr().

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

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

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

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

Почему нельзя то? Впечатление, что вам религия запрещает.

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

Какой-то гуру ляпнул

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

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

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

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

И зачем нужно «расширять концепцию»? Из любви к искусству? Это поможет проектировать программы? Чтобы что?

Тебе не надо знать «что бы что». Успокойся, выдохни, проверь типы. А вдруг там null где-то? Тоже проверь.

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

Или ответов нет?

У тебя ст головы, зачем тебе ответы, лул? Или вырази и расширь хотя бы sql на своём недоязычке, мб дойдёт.

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

какие возможности проектирования даёт «расширение концепции»

У тебя есть рбд. Тебе надо расширить понятие констрейнта, чтобы обеспечить лучшую консистентность. На твоёй субд это сделать нельзя/сложно/гемморно. Ты делаешь 3-е звено. Поздравляю, ты расширил концепцию рбд, вся система целиком начала делать то, что одна рбд не делала, не могла делать и не предполагала такого и на рбд это получилось бы через жопу. Дальше можно шлёпать сырые запросы, а можно приделать orm и еще больше расширить концепцию рбд. А можно делать не orm или не такие ублюдочные orm, как, например гибернейт, а придумывать какие-то другие свои абстракции, строить на их основании sql запросы, какие-то сложные отчеты выводить автоматически, делать вменяемые запросы к бд при этом и т.д, и т.п.

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

Какой шикарный бредогенератор.

Уровня царя.

И как этот юзер раньше мимо моего внимания прошел.

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

Вся работа современной вебни сводится к перегону байтиков в JSON и обратно:

func main() {
	data := map[string]interface{}{
		"intValue":    1234,
		"boolValue":   true,
		"stringValue": "hello!",
		"objectValue": map[string]interface{}{
			"arrayValue": []int{1, 2, 3, 4},
		},
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		fmt.Printf("could not marshal json: %s\n", err)
		return
	}

	fmt.Printf("json data: %s\n", jsonData)
}

Если эту петушню по 100 раз в день писать, то проще на Python переписать что c Go, что с Java или C#. Товарисчи нам предлагают выбросить гравер, предназначенный для ювелирной работы и взять в руки громоздкий каменный топор

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

Какие конпеляторы? Тебе дали в руки REPL для экспериментов и избавили от необходимости на каждый чих ваять интерфейсы!!!

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

в жабе изначально было «они будут такими же профи как С++вики»

но без ненужныхТМ указателей

по этому ритуал public static void не способствовал однострочникам на выброс

питон это прежде всего развитие идеи пультового(..jcl->..->sh..->python )языка c меньшим количеством трэйдоффов реализации спонтаными решениями (у Боурна(изначального) в a-z интервью как и остальных очень информативно описана экосистема языков общеизвестной лабы в которой sh c ( которое буквально от компилятор компиляторов (сс) стало с-сompiler) - в туже копилку cat cmdlist.file |ed из которой grep )

и питона не случайно # совпадает с комментами в шеле юникса

питон этот язык заместо шелла в среде где реализация «языкофлюидна» и тогда на пробу делаются на подручном шеле(perl awk и т.д.) а горячие места на фортране (ой на си)

lua,python это язык встраиваемый

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

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

ой всё

Шедровицкий ни при чём

что вы называете ООП - ссылки на первоисточники плз - т.е. что в текущем 2023 (1445(5784)) году есть краткий_курс_истинноГА_ООП(без Ясера)

если святой Фаулер утверждает не тождественное святому МакКонелу - какие текущие(и каким собором улемов утверждённые) правила резолва?

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

Такой пример будет семантически эквивалентен порождению класса через множественное наследование.

Нет.

add_tooltip(add_border(button)) != add_border(add_tooltip(button))

В первом случае всплывающее будет также и над рамкой.

А ещё можно делать

b = insert_widget(button, image).new()

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

Множественное наследование признано плохой практикой в ООП. Является ли оно тут хорошей практикой?

Оно является плохой практикой, потому что оно неуправляемо.

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

В Racket активно используется для обработки событий. Чтобы наследник не мог выкинуть обязательную часть алгоритма (проверку масок и всё такое). В Java часто эмулируют двумя связанными методами.

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

по сукществу:

по словам великих(байков наверно) решение задачи включает привнесённую инструменнтами реализацию сложность так и сложность обусловленную самой задачей

оказалось(опытным путём выяснили) что ООП(особенно в случае статических типо-классов) делает реализуемыми(в долгую) не настолько широкий область(тут класс но ) задач «реального мира»

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

всё просто

так то АДТ и утиность оно реалистичней

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

какой математикой доказано ортагональность 3 святынь ООП:

инкапсуляция 

инхеританс

инКАкойЖеМетодТоРезолвнулисьТО
qulinxao3 ★★
()
Ответ на: комментарий от ugoday

само противопоставление нуманитас vs техне ( это в том числе инь-янь римские(практики) vs гереки(теорИя) - но наоборот :))

классические универы(потомственные) vs гумбольта (найденные в пучинах)

так то не фсё так взаимо-одно-

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

ктож не пользуется - все пользуются

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

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

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

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

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

дык это уже давно не Фронтир Java - но явно не то что изначально

JAVAtm от первоСанок

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

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

тривиум(буквы-правила на них-речь)

квадриум(число-число в пространстве-число во времени-число в пространстве времени)

конечно СловоБлуд

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

О типах данных

В какой-то мере тип данных является названием области памяти, содержащей переменные заданного типа.

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

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

адепты оопэ давно уже начали плыть в сторону функциональщины с иммутабельными объектами

Вот взять последние доки к Реакту — на все голоса поют про чистые функции и неизменяемые данные. И это оплот ООПоты — пользовательские интерфейсы.

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