LINUX.ORG.RU

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

Верно подметили, речь о строгой динамической типизации. А не о конкретной реализации языка Python на С.

Речь совсем не об этом.
Сначала разберитесь в реализации внутренней архитектуры Python, а затем уже что-либо утверждайте.

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

Динамическая типизация это когда тип объекта вычисляется или может меняться (не уверен только разговор только про рантайм или есть варианты). Например строка превратилась в json, или в объект добавился/удалился метод или свойство.

Утиная типизация, это когда сопоставление интерфейсов объектов выполняется в рантайме. Например если одному объекту требуется вызвать у другого методы read и write то вызывающему глубоко начхать на тип объекта пока у вызываемого есть такие методы.

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

когда сопоставление интерфейсов объектов выполняется в рантайме

Или в компайл-тайм, как в шаблонах C++

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

Да дело не в объявлениях совсем. В некоторых статических языках тоже нет никаких объявлений

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

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

Ничего что в питоне типизация строгая, в отличии от плюсов? Это не вяжется с Вашим утверждением что в питоне нет типов (они там очень даже есть)\

В питоне типизация РАЗНАЯ. Вот о чем я хотел сказать. Динамическая строгая и утиная никакая.

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

Ничего что в питоне типизация строгая, в отличии от плюсов?

Это всего лишь значит «вместо сегфолта/продолжения работы с бредом на выходе (привет, JS) питонософт рухнет».

Суть нормальных типов — в том, что их проверяют задолго до рантайма. В питоне все проверки — в рантайме.

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

Да, тупанул, походу «в рантайме» в обоих случаях для общего случая не обязательно.

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

Суть нормальных типов — в том, что их проверяют задолго до рантайма. В питоне все проверки — в рантайме.

Что-то не припомню ни одного языка, где все проверки типов были бы при компиляции. Разве что Haskell, но даже там есть fromDynamic.

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

Что касается проверок при компиляции, то им почти не поддаются интервальные типы, типы значений «ненулевой указатель», «непустой список», «матрица с ненулевым определителем», …

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

Что-то не припомню ни одного языка, где все проверки типов были бы при компиляции.

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

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

Он, видимо, имел ввиду ситуации, при которых для одного и того же статического типа допустимы разные множества операций(тоже разыменование - nullptr vs valid pointer value)

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

Суть нормальных типов — в том, что их проверяют задолго до рантайма. В питоне все проверки — в рантайме

Мне в этом плане нравятся больше статьи из русской вики, которые говорят о типизации и не являются переводом английских, а английские говорят про эти вещи в единой статье «type checking», и в этой статье приводится деление «статическая проверка типов, динамическая проверка типов». Я же должен заметить, что тип - это не свойство данных, потому что все данные в компьютере - это одни и те же цифры. Тип - это правила работы с этими цифрами. Потому «type checking» - это проверка логических связей в правилах работы с числами, то есть, скорее «typed checking» или «типизованная проверка».

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

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

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

Утиная типизация, это когда сопоставление интерфейсов объектов выполняется в рантайме.

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

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

Суть нормальных типов — в том, что их проверяют задолго до рантайма. В питоне все проверки — в рантайме.

Что-то не припомню ни одного языка, где все проверки типов были бы при компиляции. Разве что Haskell, но даже там есть fromDynamic

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

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

граница между этапом компиляции и выполнения стирается

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

Дальше уже нет разницы, динамическая у вас типизация или статическая, и вообще что эти термины значат

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

самое главное, что софтина не падает внезапно на презентации заказчику

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

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

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

Есть REPL для C++. Питон компилируется в байткод и выполняет всегда только байткод. А что делать с кодом, сгенерированным уже во время выполнения, например, eval? И при этом всём питоний код можно AOT-скомпилировать через cython/numba/etc.

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

Нет, это просто бойцы из моей помойки высрали какую-то софтину, которая прямо на презентации отвалилась, и в итоге демонстрацию проводили по моему модулю. Это при том, что эти же люди энное время назад меня пытались учить правильно писать код. Прям вспоминается https://ru.wikipedia.org/wiki/Катастрофа_Ан-148_в_Подмосковье , где второй пилот вопреки указаниям капитана начал толкать штурвал от себя и кричать что-то вроде «ара, мамой клюнусь ат сибя надо, я фсигда так дэлаю».

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

вы это очень сильно преувеличивается. граница есть и она не сотрется в принципе, она кардинальная.

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

Во время компиляции еще AST даже не имеет конечной формы.

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

А во время выполнения уже бинарный код выполняется.

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

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

Можно прикрутить «статическую» типизацию постфактум. Как раз питон как пример.

Можно реализовать динамическую типизацию поверх любой статической GVariant в C, std::variant в C++, Dynamic в Haskell.

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

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

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

Idris ?

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

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

вы сначала дайте определение «логической корректности кода». статическая типизация определяет инварианты или ограничения на то, то вы пишете, и проверяет эти инварианты во время компиляции, если это возможно. если же это невозможно, по причинам очевидным, предлагаются опциональные(или неопциональные) рантаймовые проверки этих инвариантов. «динамическую типизацию», вообще сложно назвать типизацией. поскольку никто не собирается проверять простейших инвариантов - можно ли сложить a и б, или конкатенировать b и c. и есть ли у обьекта d метод toString(). и уж сколько обломов пожато на этой ниве, когда вдруг оказывается, что a и b сложить нельзя.

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

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

То, что уже есть в мейнстримных языках (хотя бы шарп/свифт/rust) всё же гораздо лучше, чем в питоне. Если добавить питоновскую практику «в любой непонятной ситуации бросай исключение» при том, что checked exceptions в принципе не планируются — даже аннотации типов + тот же mypy становятся заметно менее полезными. Хотя с ними и чуть легче, чем вообще без них.

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

все языки со статической типизацией. а их прилично. ну скажем так..99 процентов всяких проверок на этапе компиляции

только проверки типа отличить указатель от числа, а число от буквы.

И то: в Си число от буквы не отличается, в Паскале массивы с одинаковым количеством и типами элементов — разные типы, в Си++ невозможно указать, что параметр функции должен быть конкретного типа (подойдёт любой тип с заданным преобразованием).

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

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

взглянуть на go, common lisp, c++, d, rust, ocaml и прочие хаскели

Ты же в курсе, что CL — динамически типизированный язык?

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

поскольку никто не собирается проверять простейших инвариантов - можно ли сложить a и б, или конкатенировать b и c. и есть ли у обьекта d метод toString(). и уж сколько обломов пожато на этой ниве, когда вдруг оказывается, что a и b сложить нельзя.

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

статическая типизация определяет инварианты или ограничения на то, то вы пишете

Очень малое подмножество инвариантов (нельзя указать ни «непустой список» ни «ненулевое число» ни «отсортированный массив»). И в обмен на это крайне ограничивает возможные типы данных (нельзя в статических типах выразить ни лисповый map ни compose ни curry. Даже статические типы Typed Racket типа «список из 4 элементов с типами: целое, int32, рациональное, строка» или «функция принимающая int8 или int16 или int32 и возвращающая int16, если аргумент был int8, int32 если аргумент был int16, целое число, если аргумент был int32» в большинстве статических языков невозможны.

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

Это фича.

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

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

Да-да, один анон меня уже поправил, в обоих утверждениях «про рантайм» это лишнее уточнение к сути вопроса не имеющее отношения.

Кастану @AntonI на случай если он не читает ветку от корки до корки.

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

список из 4 элементов с типами: целое, int32, рациональное, строка

Это не список, а tuple. И это есть даже в сях, называется struct.

функция принимающая int8 или int16 или int32 и возвращающая int16, если аргумент был int8, int32 если аргумент был int16, целое число, если аргумент был int32

Перегрузка же много где есть?

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

«список из 4 элементов с типами: целое, int32, рациональное, строка» или «функция принимающая int8 или int16 или int32 и возвращающая int16, если аргумент был int8, int32 если аргумент был int16, целое число, если аргумент был int32» в большинстве статических языков невозможны.

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

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

Так в других языках слегка другие best practices. На лиспе это список потому, что так принято в лиспах. Но «список из элементов разного типа» — это не нормальный список. Юзкейс же — тупо контейнер для того, чтобы вернуть несколько значений из функции? В тот же map() такие вещи обычно не передают и оно даже не обязано быть iterable, если разобраться?

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

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

Idris ?

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

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

В обобщениях много вывода типов:

static <T> T pick(T a1, T a2) { return a2; }
Serializable s = pick("d", new ArrayList<String>());
и еще в лямбдах.

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

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

Самое интересно, что сколько ни пиши тестов на, скажем, a+b, все равно защиnbться от случая, когда в a и b попадет нечто несовместимое не получится. Просто потому что динамическая типизация по своей природе не накладывает никаких ограничений на то, какие значения могут попасть в a и b в ходе выполнения программы.

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

Омайгад, это все как раз тривиально делается: на уровень типов выносится понятие «непустой список/ненулевое число/отсортированный массив», а затем пишется функция, которая преобразует «сырой тип» в «обогащенный свойством» (хинт: функция sort может принимать «array of T» и возвращать «sorted_array of T»).

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

Любопытно, что подобие этой проблемы присутствует в стандартной библиотеке C: strchr и аналоги принимают const char*, а возвращают char*, потому что в системе типов const присобачен абы как. Но уже даже в плюсах можно написать аналогичную фунцию, которая будет сохранять константность. Кажется, даже в C11 можно.

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

Самое интересно, что сколько ни пиши тестов на, скажем, a+b, все равно защиnbться от случая, когда в a и b попадет нечто несовместимое не получится. Просто потому что динамическая типизация по своей природе не накладывает никаких ограничений на то, какие значения могут попасть в a и b в ходе выполнения программы

Ну такой себе аргумент. В Си тоже можно сделать какое-нибудь

a = 100+100;
a == -56 // true

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

В обобщениях много вывода типов:

Я сейчас сходу не вспомню пример, который вскрывает гниль дженериков (они добавляют немного проверок в compile time, а затем идут как Object в runtime с cast'ом в последний момент и сопуствующим ClassCastException), но лучше спрошу вот что: о каком выводе типов может идти речь, если невозможно без очень грязных хаков с рефлексией даже создать объект дженерикового типа?

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

Ну такой себе аргумент. В Си тоже можно сделать какое-нибудь

Что ты хотел этим показать? Никто в этом треде не говорит, что в C или даже в C++ сильная статическая типизация. Но сложить структуру с числом компилятор не позволит. И проконтролирует, что нигде в аргументах не просунут структуру вместо числа или наоборот (считаем, что все конструкторы/операторы преобразования типов explicit).

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

Я сейчас сходу не вспомню пример, который вскрывает гниль дженериков (они добавляют немного проверок в compile time, а затем идут как Object в runtime с cast'ом в последний момент и сопуствующим ClassCastException)

String[] strings = new String[10];
Object[] objects = strings;
objects[0] = new Object();

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

Чет я не понял сути проблемы.

Map<String, List<String>> myMap = new HashMap<String, List<String>>()

Где тут грязные хаки?

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

Самое интересно, что сколько ни пиши тестов на, скажем, a+b, все равно защиnbться от случая, когда в a и b попадет нечто несовместимое не получится. Просто потому что динамическая типизация по своей природе не накладывает никаких ограничений на то, какие значения могут попасть в a и b в ходе выполнения программы
Что ты хотел этим показать? Никто в этом треде не говорит, что в C или даже в C++ сильная статическая типизация. Но сложить структуру с числом компилятор не позволит. И проконтролирует, что нигде в аргументах не просунут структуру вместо числа или наоборот (считаем, что все конструкторы/операторы преобразования типов explicit)

a = Int(arg1)
b = Int(arg2)
c = a+b

Вот и защитились. Заодно, считай, и типы объявили. В стандартной либе питона такое сплошь и рядом.

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

Вот и защитились

Еще раз: эта проверка будет выполнена только в runtime. Нет ничего, что помешало бы тебе передать строки в arg1/arg2. Это частично решается аннотациями типов (не ручаюсь за точность названия), но это пришито сбоку и не особо удобно в использовании — как минимум надо натравливать сторонний чекер.

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

Еще раз: эта проверка будет выполнена только в runtime. Нет ничего, что помешало бы тебе передать строки в arg1/arg2

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

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

Вот и защитились. Заодно, считай, и типы объявили. В стандартной либе питона такое сплошь и рядом.

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

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

В любых раскладах где-то в каком-то месте нужно проводить валидацию

Если в крестах ты в объявишь прототип функции

std::string doit(Foo &foo, Bar &bar);
То нигде в программе не сможешь написать int x = doit(5, 15); — компилятор не позволит. Никаких тестов, никаких преобразований. Просто на уровне типов нельзя передать 5 вместо Foo и 15 вместо Bar, а получившийся string запихнуть в int.

Теперь понятно, о чем речь?

kawaii_neko ★★★★
()

Компилируемый python - это golang

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

Если в крестах ты в объявишь прототип функции
std::string doit(Foo &foo, Bar &bar);
То нигде в программе не сможешь написать int x = doit(5, 15); — компилятор не позволит. Никаких тестов, никаких преобразований. Просто на уровне типов нельзя передать 5 вместо Foo и 15 вместо Bar, а получившийся string запихнуть в int

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

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

ну блин… скажите пожалста, какой физсмысл имеет ваш список «целое, int32, рациональное, строка»

Аргументы для некой функции. К этим аргументам в голову будет добавлено ещё два аргумента и вызвана функция с этими аргументами.

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

Умножение для аргументов int8 должно возвращать int16, для аргументов int16 возвращать int32, для int32 возвращать int64. На ассемблере примерно так: https://www.felixcloutier.com/x86/mul

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

std::string doit(Foo &foo, Bar &bar);

Зато работает такое:

mystring doit(Foo foo, Bar bar)
{
   return std::string("ok");
}
int main()
{
  int x = doit(5, 15);
}
monk ★★★★★
()
Ответ на: комментарий от recrut

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

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