Что значит строго типизированный язык

Типизация языков программирования: разбираемся в основах

Типизация языков программирования ‌—‌ ‌это‌ ‌то,‌ ‌как‌ ‌различные‌ ‌языки‌ ‌распознают‌ ‌типы‌ ‌переменных.‌ ‌Она‌ ‌определяет,‌ ‌как‌ ‌вы‌ ‌будете‌ ‌работать‌ ‌с‌ ‌типами‌ ‌переменных:‌ ‌нужно‌ ‌ли‌ ‌их‌ ‌задавать‌ ‌изначально,‌ ‌можно‌ ‌ли‌ ‌изменять‌ ‌и‌ ‌так‌ ‌далее.‌ ‌

Виды‌ ‌типизации‌

Языки‌ ‌программирования‌ ‌бывают‌ ‌типизированными‌ ‌и‌ ‌нетипизированными‌ ‌(бестиповыми).‌ ‌

Бестиповая‌ ‌типизация‌ ‌в‌ ‌основном‌ ‌присуща‌ ‌старым‌ ‌и‌ ‌низкоуровневым‌ ‌языкам‌ ‌программирования,‌ ‌например‌ ‌‌Forth.‌ ‌‌Все‌ ‌данные‌ ‌в‌ ‌таких‌ ‌языках‌ ‌считаются‌ ‌цепочками‌ ‌бит‌ ‌произвольной‌ ‌длины‌ ‌и,‌ ‌как‌ ‌следует‌ ‌из‌ ‌названия,‌ ‌не‌ ‌делятся‌ ‌на‌ ‌типы.‌ ‌Работа‌ ‌с‌ ‌ними‌ ‌труднее,‌ ‌и‌ ‌при‌ ‌чтении‌ ‌кода‌ ‌не‌ ‌всегда‌ ‌ясно,‌ ‌о‌ ‌каком‌ ‌типе‌ ‌переменной‌ ‌идет‌ ‌речь.‌ ‌Это‌ ‌можно‌ ‌исправить,‌ ‌написав‌ ‌комментарии‌ ‌к‌ ‌коду.‌

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

Перейдём‌ ‌к‌ ‌типизированным языкам.‌ ‌

Статическая‌ ‌и‌ ‌динамическая‌ ‌типизация‌ ‌

Особенность‌ ‌языков‌ ‌программирования‌ ‌со‌ ‌статической‌ ‌типизацией‌ ‌в‌ ‌том,‌ ‌что‌ ‌проверка‌ типов‌ ‌начинается‌ ‌на‌ ‌стадии‌ ‌компиляции.‌ ‌Компиляторы‌ ‌ищут‌ ‌ошибки‌ ‌ещё‌ ‌до‌ ‌запуска‌ программы,‌ ‌и‌ ‌вам‌ ‌не‌ ‌нужно‌ ‌раз‌ ‌за‌ ‌разом‌ ‌запускать‌ ‌её,‌ ‌чтобы‌ выяснить,‌ ‌что‌ ‌пошло‌ ‌не‌ ‌так.‌ ‌Благодаря‌ ‌этому‌ ‌статически‌ ‌типизированные языки программирования ‌зачастую‌ ‌быстрее.‌ ‌Кроме‌ ‌того,‌ ‌тип‌ ‌для‌ ‌переменной‌ ‌можно‌ ‌назначить‌ ‌только‌ ‌один‌ ‌раз.‌ ‌Например‌ ‌‌в‌ ‌Java‌ ‌такая‌ ‌запись‌ ‌вызовет‌ ‌ошибку‌ ‌на‌ ‌этапе‌ ‌компиляции:‌ ‌

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

Сильная‌ ‌и‌ ‌слабая‌ ‌типизация‌ ‌

В‌ ‌слабо‌ ‌типизированных‌ ‌языках‌ ‌программирования‌ ‌можно‌ ‌смешивать‌ ‌разные‌ ‌типы‌ ‌данных. Так‌ ‌код‌ ‌получается‌ ‌короче‌ ‌—‌ ‌язык‌ ‌«старается‌»‌ ‌сам‌ ‌выполнять‌ ‌операции‌ ‌преобразования‌ ‌с‌ ‌разными‌ ‌типами.‌ ‌Впрочем,‌ ‌в‌ ‌таком‌ ‌случае‌ ‌не‌ ‌всегда‌ ‌ясно,‌ ‌как‌ ‌поведёт‌ ‌себя‌ ‌программа.‌ ‌Например,‌ ‌в‌ ‌JavaScript‌ ‌возможна‌ ‌такая‌ ‌запись:‌ ‌

При‌ ‌сильной‌ ‌или‌ ‌строгой‌ ‌типизации,‌ ‌как‌ ‌в‌ ‌Python,‌ ‌язык‌ ‌не‌ ‌позволяет‌ ‌смешивать‌ ‌разные‌ ‌типы‌ ‌—‌ ‌то‌ ‌есть,‌ ‌если‌ ‌вы‌ ‌обозначили‌ ‌переменную‌ ‌как‌ ‌число,‌ ‌то‌ ‌добавить‌ ‌к‌ ‌ней‌ ‌строку‌ ‌уже‌ ‌не‌ ‌получится:‌ ‌ ‌

Языки‌ ‌с‌ ‌сильной‌ ‌типизацией‌ ‌надёжнее.‌ ‌Да и‌ ‌программист,‌ ‌прописывая‌ ‌все‌ ‌преобразования‌ ‌вручную,‌ ‌лучше‌ ‌понимает,‌ ‌как‌ ‌работает‌ ‌его‌ ‌код.‌ ‌

Явная‌ ‌и‌ ‌неявная‌ ‌типизация‌ ‌

В‌ ‌языках‌ ‌программирования‌ ‌с‌ ‌явной‌ ‌типизацией‌ ‌типы‌ ‌переменных‌ ‌и‌ ‌возвращаемых‌ ‌значений‌ ‌функций‌ ‌нужно‌ ‌задавать.‌ ‌Это‌ ‌дольше,‌ ‌но‌ ‌так‌ ‌проще‌ ‌определять,‌ ‌что‌ ‌значат‌ ‌все‌ ‌данные,‌ ‌а‌ ‌программисту‌ ‌не‌ ‌придётся‌ ‌запоминать‌ ‌или‌ ‌записывать‌ ‌отдельно‌ ‌каждое‌ ‌значение.‌ ‌В‌ ‌языке‌ ‌С‌ ‌переменную‌ ‌нужно‌ ‌записывать‌ ‌так:‌ ‌

При‌ ‌неявной‌ ‌типизации‌ ‌тип‌ ‌переменной‌ ‌определяется‌ ‌интерпретатором‌ ‌или‌ ‌компилятором,‌ ‌поэтому‌ ‌записи‌ ‌в‌ ‌таких‌ ‌языках‌ ‌короче.‌ ‌Иногда‌ ‌они‌ ‌позволяют‌ ‌вручную‌ ‌указывать‌ ‌типы‌ ‌значений,‌ ‌как‌ ‌в‌ ‌Haskell‌ ‌или‌ ‌Python.‌ ‌В‌ ‌Python‌ ‌возможна‌ ‌такая‌ ‌запись,‌ ‌ведь‌ ‌язык‌ ‌сам‌ ‌определит,‌ ‌что‌ ‌это‌ ‌целое‌ ‌число:‌ ‌

Типизация‌ ‌в‌ ‌разных‌ ‌языках‌ ‌программирования‌ ‌

Что значит строго типизированный язык

Разные‌ ‌категории ‌могут‌ ‌пересекаться.‌ ‌Обычно‌ ‌языку‌ ‌программирования‌ ‌присуще‌ ‌одно‌ ‌значение‌ ‌из‌ ‌каждой‌ ‌группы:‌ ‌он‌ ‌может‌ ‌быть‌ ‌сильным‌ ‌или‌ ‌слабым,‌ ‌явным‌ ‌или‌ ‌неявным.‌ ‌Но‌ ‌есть‌ ‌несколько‌ ‌исключений.‌ ‌Так,‌ ‌Python‌ ‌с‌ ‌аннотациями‌ ‌может‌ ‌поддерживать‌ ‌явную‌ ‌типизацию‌,‌ ‌а‌ ‌язык‌ ‌D,‌ ‌наоборот,‌ ‌‌—‌ ‌неявную.‌ ‌С#‌ ‌поддерживает‌ ‌динамическую‌ ‌типизацию‌ ‌благодаря‌ ‌ключевым‌ ‌словам‌ ‌‌dynamic‌ ‌и‌ ‌var,‌ ‌Delphi‌ ‌—‌ ‌благодаря‌ ‌типу‌ ‌Variant,‌ ‌а‌ ‌С++‌ ‌—‌ ‌‌с‌ ‌помощью‌ ‌библиотеки‌ ‌Boost‌ ‌и‌ ‌имеет‌ ‌одновременно‌ ‌черты‌ ‌языка‌ ‌с‌ ‌сильной‌ ‌и‌ ‌
слабой‌ ‌типизацией.‌

Источник

Типизация в языках

Давайте наведем порядок в голове

Что значит строго типизированный язык

Александр Майоров — FullStack CTO

Aug 4, 2016 · 3 min read

Языки программирования по типизации делятся на два больших лагеря — типизированные и не типизированные.

Типизированные языки

К типизированным языкам относятся

Не типизированные языки

Да-да, именно так. JavaScript, как и многие другие интерпретируемые языки — типизированный. Поэтому ни в коем случае не говорите, что он не типизированный. Особенно на собеседованиях!

В свою очередь, типизированные языки разделяются еще на несколько пересекающихся категорий:

Языки со статической типизацией

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

Представители: C/C++, C#, Java.

TypeScript — язык со статической типизацией.

Языки с динамической типизацией

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

Представители: JavaScript, PHP, Python, Ruby…

Строгая типизация (сильная)

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

Представители: Java, Python, Haskell, Lisp…

Кстати TypeScript добавляет строгую типизацию и не позволяет смешивать типы:

Нестрогая типизация (слабая)

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

Представители: JavaScript (йаху!), Lua, Visual Basic, PHP…

Явная типизация

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

Представители: C++, D, C#…

Кстати TypeScript — язык с явной и неявной строгой типизацией одновременно. Неявная типизация досталась от JS (так как TS — это JS). А вот фича и особенность TS — это его природа явно типизированного языка.

Неявная типизация

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

Представители: JavaScript (еее!), PHP, Lua, Python ( хотя с версии 3 есть аннотации типов, которые как бы добавляют явную типизацию)…

В таких языках, как правило, у объектов существуют специальные методы, вызываемые при приведении к типу. К примеру, в PHP есть метод _toString(), а в JavaScript одноименный метод, но без подчеркивания — toString(). Эти методы вызываются при приведении объекта к строковому типу. Иногда такие методы называют магическими (любые неявные процессы — это всегда магия).

Важно заметить, что все эти категории пересекаются. Исходя из этих категорий, получаем, что JavaScript имеет динамическую неявную типизацию. При этом TypeScript имеет статическую строгую явную типизацию с возможностями неявно типизированного языка, но компилируется (транслируется/транспилируется) в рантайм код с неявной динамической типизацией.

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

Источник

Что такое типизация в программировании

Объясняем, что это такое, какая бывает типизация и на что она влияет.

Что значит строго типизированный язык

Что значит строго типизированный язык

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

Типизация — это то, как язык распознаёт типы переменных. Типизация определяет, нужно ли вам писать тип, или язык «поймёт» его сам, и насколько свободно можно с типами работать: например, можно ли их менять.

Что значит строго типизированный язык

В бэкграунде — программирование, французский язык, академическое рисование, капоэйра. Сейчас учит финский. Любит путешествия и Балтийское море.

А что такое типы?

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

Типы бывают разные и немного различаются в разных языках.

Зачем нужно знать о типизации?

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

Какая бывает типизация?

Слабая и сильная

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

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

В JavaScript, языке со слабой типизацией, можно сложить строку с числом, например вот так:

И получить строку «21».

А в Java так сделать нельзя: появится ошибка.

Что значит строго типизированный язык

Статическая и динамическая

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

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

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

Например, в Python (динамическая типизация) можно сделать вот так:

И язык не будет возражать. В Java (статическая типизация) так сделать нельзя.

Явная и неявная

Ещё типизацию делят на явную и неявную. Когда типизация неявная, тип определяется сам в момент, когда вы записываете в переменную информацию.

Например, если в Python написать так:

Он прочитает, что вы записали в переменную b целое число, и определит b как integer (int).

Явная типизация значит, что тип переменной написан. Например, в С переменная записывается вот так:

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

Как типизация влияет на работу с языком?

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

В каких языках какая типизация

Напоследок — типизации некоторых популярных языков:

Если вы пока новичок в программировании, то приглашаем вас на наш курс «Python-разработчик». Потому что Python — один из лучших языков для начинающих, в том числе благодаря своей типизации. Он научит дисциплине в работе с переменными, но при этом не уморит долгой писаниной. В курсе расскажут подробнее о разных типах переменных и динамической типизации, научат работе с классами и объектами и познакомят с разными библиотеками.

Источник

Строгая типизация

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

Строгая типизация подразумевает выполнение двух обязательных условий:

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

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

Источник

Статическая и динамическая типизация

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

Что значит строго типизированный язык

Тип — это коллекция возможных значений. Целое число может обладать значениями 0, 1, 2, 3 и так далее. Булево может быть истиной или ложью. Можно придумать свой тип, например, тип «ДайПять», в котором возможны значения «дай» и «5», и больше ничего. Это не строка и не число, это новый, отдельный тип.

Динамически типизированные языки помечают значения типами: язык знает, что 1 это integer, 2 это integer, но он не может знать, что переменная x всегда содержит integer.

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

Статически типизированные языки

Статические языки проверяют типы в программе во время компиляции, еще до запуска программы. Любая программа, в которой типы нарушают правила языка, считается некорректной. Например, большинство статических языков отклонит выражение «a» + 1 (язык Си — это исключение из этого правила). Компилятор знает, что «a» — это строка, а 1 — это целое число, и что + работает только когда левая и правая часть относятся к одному типу. Так что ему не нужно запускать программу чтобы понять, что существует проблема. Каждое выражение в статически типизированном языке относится к определенному типу, который можно определить без запуска кода.

Это не уменьшает «статичность» системы типов. Система типов в Haskell знаменита своей статичностью, строгостью и мощностью, и в по всем этим фронтам Haskell опережает Java.

Динамически типизированные языки

Динамически типизированные языки не требуют указывать тип, но и не определяют его сами. Типы переменных неизвестны до того момента, когда у них есть конкретные значения при запуске. Например, функция в Python

может складывать два целых числа, склеивать строки, списки и так далее, и мы не можем понять, что именно происходит, пока не запустим программу. Возможно, в какой-то момент функцию f вызовут с двумя строками, и с двумя числами в другой момент. В таком случае x и y будут содержать значения разных типов в разное время. Поэтому говорят, что значения в динамических языках обладают типом, но переменные и функции — нет. Значение 1 это определенно integer, но x и y могут быть чем угодно.

Сравнение

Большинство динамических языков выдадут ошибку, если типы используются некорректно (JavaScript — известное исключение; он пытается вернуть значение для любого выражения, даже когда оно не имеет смысла). При использовании динамически типизированных языков даже простая ошибка вида «a» + 1 может возникнуть в боевом окружении. Статические языки предотвращают такие ошибки, но, конечно, степень предотвращения зависит от мощности системы типов.

Сильная и слабая типизация

Понятия «сильный» и «слабый» — очень неоднозначные. Вот некоторые примеры их использования:

Иногда «сильный» означает «статический».
Тут все просто, но лучше использовать термин «статический», потому что большинство используют и понимают его.

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

Давайте остановимся. Вот как некоторые языки отвечают этим определениям. Как можно заметить, только Haskell последовательно «сильный» по всем параметрам. Большинство языков не такие четкие.

ЯзыкСтатический?Неявные преобразования?Строгие правила?Безопасный для памяти?
CСильныйКогда какСлабыйСлабый
JavaСильныйКогда какСильныйСильный
HaskellСильныйСильныйСильныйСильный
PythonСлабыйКогда какСлабыйСильный
JavaScriptСлабыйСлабыйСлабыйСильный

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

Зачастую термины «сильный» и «слабый» относятся к неопределенной комбинации разных определений выше, и других, не показанных здесь определений. Весь этот беспорядок делает слова «сильный» и «слабый» практически бессмысленными. Когда хочется использовать эти термины, то лучше описать, что конкретно имеется ввиду. Например, можно сказать, что «JavaScript возвращает значение, когда складывается строка с числом, но Python возвращает ошибку». В таком случае мы не будем тратить свои силы на попытки прийти к соглашению о множестве значений слова «сильный». Или, еще хуже: придем к неразрешенному непониманию из-за терминологии.

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

Сильная типизация: Система типов, которую я люблю и с которой мне комфортно.

Слабая типизация: Система типов, которая беспокоит меня или с которой мне не комфортно.

Постепенная типизация (gradual typing)

Неудовлетворительное решение на практике — это задать выражению eval() тип Any, что напоминает Object в некоторых объектно-ориентированных языках программирования или интерфейс interface <> в Go: это тип, которому удовлетворяет любое значение.

В некоторых языках есть опциональная или постепенная типизация (gradual typing): они динамические по умолчанию, но позволяют добавлять некоторые статические аннотации. В Python недавно добавили опциональные типы; TypeScript — это надстройка над JavaScript, в котором есть опциональные типы; Flow производит статический анализ старого доброго кода на JavaScript.

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

Компиляция статически типизированного кода

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

Компиляторы статических языков обычно могут генерировать более быстрый код, чем компиляторы динамических. Например, если компилятор знает, что функция add принимает целые числа, то он может использовать нативную инструкцию ADD центрального процессора. Динамический язык будет проверять тип при выполнении, выбирая один из множества функций add в зависимости от типов (складываем integers или floats или склеиваем строки или, может быть, списки?) Или нужно решить, что возникла ошибка и типы не соответствуют друг другу. Все эти проверки занимают время. В динамических языках используются разные трюки для оптимизации, например JIT-компиляция (just-in-time), где код перекомпилируется при выполнении после получения всей необходимой о типах информации. Однако, никакой динамический язык не может сравниться по скоростью с аккуратно написанным статическим кодом на языке вроде Rust.

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

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

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

Существуют разные подходы с разными уровнями безопасности, но Python и JavaScript оба являются динамически типизированными языками.

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

Haskell же не позволит сложить integer и float без явного преобразования перед этим. Си и Haskell оба являются статически типизированными, не смотря на такие большие отличия.

Есть множество вариаций динамических и статических языков. Любое безоговорочное высказывание вида «статические языки лучше, чем динамические, когда дело касается Х» — это почти гарантированно ерунда. Это может быть правдой в случае конкретных языков, но тогда лучше сказать «Haskell лучше, чем Python когда дело касается Х».

Разнообразие статических систем типизации

Давайте взглянем на два знаменитых примера статически типизированных языков: Go и Haskell. В системе типизации Go нет обобщенных типов, типов с «параметрами» от других типов. Например, можно создать свой тип для списков MyList, который может хранить любые нужные нам данные. Мы хотим иметь возможность создавать MyList целых чисел, MyList строк и так далее, не меняя исходный код MyList. Компилятор должен следить за типизацией: если есть MyList целых чисел, и мы случайно добавляем туда строку, то компилятор должен отклонить программу.

Go специально был спроектирован таким образом, чтобы невозможно было задавать типы вроде MyList. Лучшее, что возможно сделать, это создать MyList «пустых интерфейсов»: MyList может содержать объекты, но компилятор просто не знает их тип. Когда мы достаем объекты из MyList, нам нужно сообщить компилятору их тип. Если мы говорим «Я достаю строку», но в реальности значение — это число, то будет ошибка исполнения, как в случае с динамическими языками.

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

Haskell может выражать намного более сложные идеи напрямую типами. Например, Num a => MyList a означает «MyList значений, которые относятся к одному типу чисел». Это может быть список integer’ов, float’ов или десятичных чисел с фиксированной точностью, но это определенно никогда не будет списком строк, что проверяется при компиляции.

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

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

Сравните эту мощность с Go, который не способен выразить простую идею MyList, не говоря уже о «функции, которая принимает два аргумента, и они оба численные и одного типа, и которая делает ввод/вывод».

Подход Go упрощает написание инструментов для программирования на Go (в частности, реализация компилятора может быть простой). К тому же, требуется изучить меньше концепций. Как эти преимущества сравнимы со значительными ограничениями — субъективный вопрос. Однако, нельзя поспорить, что Haskell сложнее изучить, чем Go, и что система типов в Haskell намного мощнее, и что Haskell может предотвратить намного больше типов багов при компиляции.

Go и Haskell настолько разные языки, что их группировка в один класс «статических языков» может вводить в заблуждение, не смотря на то, что термин используется корректно. Если сравнивать практические преимущества безопасности, то Go ближе к динамических языкам, нежели к Haskell’у.

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

Конкретные примеры отличия в возможностях систем типизации

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

В Go можно сказать «функция add принимает два integer’а и возвращает integer»:

В Haskell можно сказать «функция принимает любой численный тип и возвращает число того же типа»:

В Idris можно сказать «функция принимает два integer’а и возвращает integer, но первый аргумент должен быть меньше второго аргумента»:

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

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

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

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

Группа два (Java и C#) — это мэйнстримовые языки, зрелые и широко используемые.

Группа три находится на пороге входа в мэйнстрим, с большой поддержкой со стороны Mozilla (Rust) и Apple (Swift).

Группа четыре (Idris and Agda) далеки от мэйнстрима, но это может измениться со временем. Языки группы три были далеко от мэйнстрима еще десять лет назад.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *