LINUX.ORG.RU

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

 ,


1

4

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

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

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

Используем try (речь конечно о скриптовых ЯП) и non problem.

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

обойди его, поищи что-нибудь.

Мне не нужно обходить всё и искать что-нибудь, у меня уже есть объект. Я буду просто доставать нужные поля.

ya-betmen ★★★★★
()
Ответ на: комментарий от Forum0888

Python для «первого подхода» ака прототипа

при нынешней моще телефонов не гойворя о десктопах

если алго совсем не суперекспонента

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

а вот когда не хватат - можно и mojo :)

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

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

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

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

Никто не мешает в ст добавить тип ИзменяемыйОбъект с методами «добавить поле» и «удалить поле».

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

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

Мне не нужно обходить всё и искать что-нибудь

Не нужно или не можешь и поэтому не нужно?

Я буду просто доставать нужные поля.

Доставать что бы что? Сложить в другой объект? Не, мб и нет, но довольно большой объем работы заключается именно в перекладывании в поля/конструктор/метод. И если просто поднять голову из ст болота и маленько осмотреться, то окажется, что простое key[value] позволяет многую работу просто не делать.

crutch_master ★★★★★
()

Статическая типизация запрещает складывать носорогов с апельсинами. В принципе, хорошее дело, но важно ли это? По-моему, востребованность такой защиты сильно преувеличенна. Нужно быть конченным, чтобы случайно перепутать true и "true". А вот от реальных проблем реального мира статика помогает плохо. Например, мне нужно, чтобы getIP() вернула корректный IP адрес. Мне не нужно беззнаковое 32-битное целое, мне нужен корректный IP либо ошибка, если адрес неправильный. При этом что такое правильный адрес — вопрос нетривиальный и в разное время ответ может разниться. И ваша статика никак мне не поможет решить эту проблему.

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

Там есть куча всяких валидаторов, чтобы всё было на месте, это не то, чтобы какая-то фундаментальная проблема.

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

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

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

Если на компилятор ограничений нет, то скорость программы практически одинакова.

так что если начать юзать хешмапы в хвост и в гриву, быстро обнаружишь, что изобрёл что-то похожее жс/луа/пистон

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

Я так программу с Перла на Си переписывал. Сначала сделал один-в-один. Ускорение примерно в два раза. Потом обнаружил, что ключи строго определённые. Переделал на массив от enum, стало ещё в 20 раз быстрее.

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

я однажды программу из с++ в с++ переделывал… оказалось в 100 раз! быстрее

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

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

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

Например, мне нужно, чтобы getIP() вернула корректный IP адрес.

Пишешь функцию проверки адреса с сигнатурой

checkIP :: IP -> Maybe CorrectIP

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

Пишешь сигнатуру

getIP :: CorrectIP

Отдаёшь на реализацию. У тебя есть гарантия, что getIP где-то вызовет проверку и эта проверка вернёт корректный IP.

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

Не нужно или не можешь и поэтому не нужно?

Обычно это просто не нужно.

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

ya-betmen ★★★★★
()
Ответ на: комментарий от Siborgium

Она не имплицитная. Амперсанд ты сам должен написать. И сам должен написать, какой тип возвращает амперсанд. Никакой имплицитности.

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

Угу, бывает.
Первая реализация парсинга глобального модуля была где-то 3000 строк.
После переработки алгоритма 500 строк.
При этом он правильно парсит также метаданные диалоговых форм любого объекта (C++).
Результат парсинга помещается в десятки разных структур.
То бишь обобщённый алгоритм на C++ с использованием API для использования метаданных.

Немного «подшерстить» и для 8.x подойдёт.

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

Видимо, с первого раза не дошло. Ок. Продолжаем.

https://godbolt.org/z/6zf65dvz6


use std::ops::Deref;

struct A {}
impl A {
    fn fun(&self) -> () { println!("a_fun"); }
}

struct B {}
impl B {
    fn fun(&self) -> () { println!("b_fun") }
}
impl Deref for B {
    type Target = A;
    fn deref(&self) -> &Self::Target { &A{} }
}

struct C{}
impl Deref for C {
    type Target = B;
    fn deref(&self) -> &Self::Target { &B{} }
}

fn a_fun(a: &A) -> () {
    a.fun()
}

fn b_fun(b: &B) -> () {
    b.fun()
}

pub fn main(){
    let c = C{};
    a_fun(&c);
    b_fun(&c)
}

Вывод

a_fun
b_fun

Что полностью соответствует критерию из сообщения Зачем нужна статическая типизация?, или Вы всё врете! (комментарий)

f(b); // здесь он b.operator_A()
g(b); // здесь он b.operator_С()

Интересно, какие отговорки найдутся на это?

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

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

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

Скорость и типизация нынче мало связаны.

Хешмапа - уже не имеет к этому отношения. Для дт не все динамические объекты - это хешмапы, для ст - единственная возможность менять состав полей - мапа (ну или изобрести заново свой дт язычок/дт объекты внутри). Само собой, что мапа будет сливать по скорости доступа к «полю» по сравнению с норм. объектом.

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

Ну вот. Если задача не выходит за рамки стат. структур - то всё замечательно, но шаг в сторону и там всё.

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

всегда можно взять ассоциативныйй масив с ассоциативными массивами.

Ага. И я даже спрашивать не буду, как в таком случае выглядеть аналоги x?.y?.z?.find?.(...) или что-то сложнее.

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

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

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

Ни хрена себе изврат…

Наследование, но очень странное.

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

class C
{
  C(A a);
  C(B b);
}

если A и B друг про друга не знают?

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

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

В смысле, валидатор работает уже у пользователя при работе программы? А толку тогда? Просто чуть раньше получишь «поле ХХХ не найдено». А в ст эта ошибка была бы на стадии компиляции и до пользователя не дошла.

для ст - единственная возможность менять состав полей - мапа (ну или изобрести заново свой дт язычок/дт объекты внутри)

Достаточно добавить тип «объект с изменяемыми полями». Ст часто уже содержит в себе дт, изобретать не надо.

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

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

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

Ну вот. Если задача не выходит за рамки стат. структур - то всё замечательно, но шаг в сторону и там всё.

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

и что удивительно - накакого «шаг в сторону и там все» не наблюдается.

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

Не динамические. Данные динамические, а структуры статические описаны синтаксисом языка Си. Вот если бы этот компилятор мог читать синтаксис из конфигурационного файла, были бы динамические.

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

Это интересно. Откуда компилятор узнает, что такое CorrectIP? Положимъ, что это IP из заданного диапазона, который получается из конфига, переменной окружения или аргумента командной строки. Т.е. на момент компиляции просто не существует.

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

Угу.
Когда в run-time можно будет динамически прочитать и использовать 10000 разных структур, тогда это динамика.
Но в C++ этого никогда не будет потому, что в стандартах балобольство, что на форумах.
Особенно любят «щёки понадувать» о метаданных, в которых толком ничего не понимают.

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

Вот если бы этот компилятор мог читать синтаксис из конфигурационного файла

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

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

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

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

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

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

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

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

Когда в run-time можно будет динамически прочитать и использовать 10000 разных структур, тогда это динамика.

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

разве это сложно?

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

Ага, понял вашу мысль. Возражение снимается.

P.S. Но монады пугают куда больше, нежели любые проблемы с динамической типизацией.

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

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

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

Я бы всё-таки этот случай отнёс к наследованию

Это не наследование. В каком-то смысле это позволяет эмулировать наследование реализации, но не более.

Работает это очень просто: deref в расте вызывается имплицитно. Т.е. это та самая эксплицитно определяемая, но имплицитно вызываемая конверсия.

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

Предлагаю создать отдельный топик по данному вопросу.

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

Сущие пустяки.

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

И в алгоритмах core всю эту работу выполняет само.
В C++ этого и близко нет.

Так-то вы правы.
Всё можно разработать.

Надеюсь, что после GUI начну разработку API для использования баз знаний.

Поэтому-то и хвалю всегда Си и C++, так как они прекрасно подходят для системного программирования.

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

P.S. Но монады пугают куда больше, нежели любые проблемы с динамической типизацией.

Вот C++:

;; correctip.hpp
class CorrectIP {
   friend std::optional<CorrectIP> checkIP(IP ip);
public:
   ...
private:
   CorrectIP();
}

extern std::optional<CorrectIP> checkIP(IP ip);

;; getip.hpp
extern CorrectIP getIP();
monk ★★★★★
()
Ответ на: комментарий от alysnix

Так в лиспе также. Только видов узлов AST намного меньше. Добавить узел AST с структурой, например for(...;...;...) { ... } нельзя.

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

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

на плюсах примерно такой очевидный интерфейс динамической декларации класса:

auto lc = add_class("my_dynamic_class");
lc->add_field("x", IntType);
lc->add_field("y", IntType);
lc->add_field("z", IntType);
...
lc->end_class();

примерно так работает компилятор, когда читает декларацию вашего типа.

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

зы. чисто оценочно полная поддержка таких динамических типов и работа с ними, это не более 1000 строк, ну 2000 строк, это уже какой-то теоретический даже предел.

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

Вы правы.

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

Например, объект содержит метаданные и данные какой-нибудь конфигурации 1С 8.3.

На словах всё ПРОСТО!

Но по сути всё же Вы правы.
Всё можно!

Это вот как в развитии C++, трепятся, трепятся, трепятся и обещают, обещают, обещают.

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

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

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

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

У Вас такая архитектура, у меня совершенно иная.
Не в этом суть.

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

Что касаемо реализации, то здесь важна, прежде всего, эффективная архитектура для работы API core.

Вот почему Python тупит?

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

Вот почему Python тупит?

глагол «тупить» не имеет инженерного смысла. не знаю почему тупит питон. может потому что ключница делала.

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

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

то есть вопрос не только в языке.

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

Безусловно, так в постах и не акцентировал на то, что ЯП плох, а вот архитектура объектной системы и реализация всё же «не очень».

Sorry, в каком посте утверждал, что ЯП является причиной медленной работы?

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

В этом случае там будет понятный объект с понятной структурой который можно распарсить.

Хотя я и встречал в том числе и хмлки наклееные руками мимо схемы. Но это к счастью не является основным подходом.

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

Форумчан прошу простить моё балобольство и продолжить обсуждение.
Польза от этого будет всем нам!

Тема треда ведь ОGO Go как интересна.

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